Skip to content

Commit f1dbac9

Browse files
authored
Merge pull request mantinedev#11 from widgeter/new-components
Include a Contact page
2 parents a66e3d9 + 696f1b9 commit f1dbac9

File tree

11 files changed

+446
-146
lines changed

11 files changed

+446
-146
lines changed

app/contact/page.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Contact2 from '~/components/widgets/Contact2';
2+
import Features2 from '~/components/widgets/Features2';
3+
import { featuresData1 } from '~/shared/data';
4+
5+
const Page = () => {
6+
return (
7+
<>
8+
<Contact2 />
9+
<Features2 {...featuresData1} />
10+
</>
11+
);
12+
};
13+
14+
export default Page;

app/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import FAQs2 from '~/components/widgets/FAQs2';
88
import Pricing from '~/components/widgets/Pricing';
99
import Team from '~/components/widgets/Team';
1010
import CallToAction2 from '~/components/widgets/CallToAction2';
11-
import { content2Data, contentData } from '~/shared/data';
11+
import { content2Data, contentData, featuresData } from '~/shared/data';
1212
import Contact from '~/components/widgets/Contact';
1313

1414
export default function Page() {
1515
return (
1616
<>
1717
<Hero />
1818
<SocialProof />
19-
<Features3 />
19+
<Features3 {...featuresData} />
2020
<Content {...contentData} />
2121
<Content {...content2Data} />
2222
<Steps />

src/components/common/Form.tsx

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import { FormProps } from '../../shared/types';
5+
6+
const Form = ({ title, description, inputs, radioBtns, textarea, checkboxes, btn, btnPosition }: FormProps) => {
7+
const [inputValues, setInputValues] = useState([]);
8+
const [radioBtnValue, setRadioBtnValue] = useState('');
9+
const [textareaValues, setTextareaValues] = useState('');
10+
const [checkedState, setCheckedState] = useState<boolean[]>(new Array(checkboxes && checkboxes.length).fill(false));
11+
12+
// Update the value of the entry fields
13+
const changeInputValueHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
14+
const { name, value } = event.target;
15+
16+
setInputValues({
17+
...inputValues,
18+
[name]: value,
19+
});
20+
};
21+
22+
// Update checked radio buttons
23+
const changeRadioBtnsHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
24+
setRadioBtnValue(event.target.value);
25+
};
26+
27+
// Update the textarea value
28+
const changeTextareaHandler = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
29+
setTextareaValues(event.target.value);
30+
};
31+
32+
// Update checkbox radio buttons
33+
const changeCheckboxHandler = (index: number) => {
34+
setCheckedState((prevValues) => {
35+
const newValues = [...(prevValues as boolean[])];
36+
newValues.map(() => {
37+
newValues[index] = !checkedState[index];
38+
});
39+
return newValues;
40+
});
41+
};
42+
43+
return (
44+
<div className="card h-fit max-w-6xl p-5 md:p-12" id="form">
45+
{title && <h2 className={`${description ? 'mb-2' : 'mb-4'} text-2xl font-bold`}>{title}</h2>}
46+
{description && <p className="mb-4">{description}</p>}
47+
<form id="contactForm">
48+
<div className="mb-6">
49+
{/* Inputs */}
50+
<div className="mx-0 mb-1 sm:mb-4">
51+
{inputs.map(({ type, label, name, placeholder }, index) => (
52+
<div key={`item-input-${index}`} className="mx-0 mb-1 sm:mb-4">
53+
<label htmlFor={name} className="pb-1 text-xs uppercase tracking-wider">
54+
{label}
55+
</label>
56+
<input
57+
type={type}
58+
name={name}
59+
value={inputValues[index]}
60+
onChange={changeInputValueHandler}
61+
placeholder={placeholder}
62+
className="mb-2 w-full rounded-md border border-gray-400 py-2 pl-2 pr-4 shadow-md dark:text-gray-300 sm:mb-0"
63+
/>
64+
</div>
65+
))}
66+
</div>
67+
{/* Radio buttons */}
68+
{radioBtns && (
69+
<div className="mx-0 mb-1 sm:mb-3">
70+
<label className="pb-1 text-xs uppercase tracking-wider">{radioBtns?.label}</label>
71+
<div className="flex flex-wrap">
72+
{radioBtns.radios.map(({ label }, index) => (
73+
<div key={`radio-btn-${index}`} className="mr-4 items-baseline">
74+
<input
75+
type="radio"
76+
name={label}
77+
value={`value${index}`}
78+
checked={radioBtnValue === `value${index}`}
79+
onChange={changeRadioBtnsHandler}
80+
className="cursor-pointer"
81+
/>
82+
<label className="ml-2">{label}</label>
83+
</div>
84+
))}
85+
</div>
86+
</div>
87+
)}
88+
{/* Textarea */}
89+
{textarea && (
90+
<div className={`mx-0 mb-1 sm:mb-4`}>
91+
<label htmlFor={textarea.name} className="pb-1 text-xs uppercase tracking-wider">
92+
{textarea.label}
93+
</label>
94+
<textarea
95+
name={textarea.name}
96+
cols={textarea.cols}
97+
rows={textarea.rows}
98+
value={textareaValues}
99+
onChange={(e) => changeTextareaHandler(e)}
100+
placeholder={textarea.placeholder}
101+
className="mb-2 w-full rounded-md border border-gray-400 py-2 pl-2 pr-4 shadow-md dark:text-gray-300 sm:mb-0"
102+
/>
103+
</div>
104+
)}
105+
{/* Checkboxes */}
106+
{checkboxes && (
107+
<div className="mx-0 mb-1 sm:mb-4">
108+
{checkboxes.map(({ label }, index) => (
109+
<div key={`checkbox-${index}`} className="mx-0 my-1 flex items-baseline">
110+
<input
111+
type="checkbox"
112+
name={label}
113+
checked={checkedState[index]}
114+
onChange={() => changeCheckboxHandler(index)}
115+
className="cursor-pointer"
116+
/>
117+
<label className="ml-2">{label}</label>
118+
</div>
119+
))}
120+
</div>
121+
)}
122+
</div>
123+
<div
124+
className={`${btnPosition === 'left' ? 'text-left' : btnPosition === 'right' ? 'text-right' : 'text-center'}`}
125+
>
126+
<button type={btn.type} className="btn btn-primary sm:mb-0">
127+
{btn.title}
128+
</button>
129+
</div>
130+
</form>
131+
</div>
132+
);
133+
};
134+
135+
export default Form;

src/components/widgets/Contact.tsx

+3-35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { contactData } from '~/shared/data';
2+
import Form from '../common/Form';
23
import HeaderWidget from '../common/HeaderWidget';
34

45
const Contact = () => {
@@ -9,7 +10,7 @@ const Contact = () => {
910
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:px-8 lg:py-20">
1011
{header && <HeaderWidget header={header} titleClassname="text-3xl sm:text-5xl" />}
1112
<div className="flex items-stretch justify-center">
12-
<div className="grid md:grid-cols-2 md:items-center">
13+
<div className="grid md:grid-cols-2">
1314
<div className="h-full pr-6">
1415
{content && <p className="mt-3 mb-12 text-lg text-gray-600 dark:text-slate-400">{content}</p>}
1516
<ul className="mb-6 md:mb-0">
@@ -37,40 +38,7 @@ const Contact = () => {
3738
))}
3839
</ul>
3940
</div>
40-
<div className="card h-full p-5 md:p-8">
41-
<h2 className="text-2xl font-bold">{form.title}</h2>
42-
<p>{form.description}</p>
43-
<form id="contactForm" className="mt-4">
44-
<div className="mb-4">
45-
<div className="my-1 grid grid-cols-1 md:my-0">
46-
{form.inputs.map(({ type, name, placeholder }, index) => (
47-
<div key={`item-input-${index}`} className="my-1 mx-0">
48-
<input
49-
type={type}
50-
name={name}
51-
placeholder={placeholder}
52-
className="w-full rounded-md border border-gray-400 py-2 pl-2 pr-4 shadow-md dark:text-gray-300"
53-
/>
54-
<div className="invalid-feedback" style={{ display: 'block' }}></div>
55-
</div>
56-
))}
57-
<div className="mx-0 my-1">
58-
<textarea
59-
name="text"
60-
cols={30}
61-
rows={5}
62-
placeholder="Write your message..."
63-
className="w-full rounded-md border border-gray-400 py-2 pl-2 pr-4 shadow-md dark:text-gray-300"
64-
></textarea>
65-
<div className="invalid-feedback" style={{ display: 'block' }}></div>
66-
</div>
67-
</div>
68-
</div>
69-
<button type={form.btn.type} className="btn btn-primary sm:mb-0">
70-
{form.btn.title}
71-
</button>
72-
</form>
73-
</div>
41+
<Form {...form} btnPosition="center" />
7442
</div>
7543
</div>
7644
</div>

src/components/widgets/Contact2.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { contact2Data } from '~/shared/data';
2+
import Form from '../common/Form';
3+
import HeaderWidget from '../common/HeaderWidget';
4+
5+
const Contact2 = () => {
6+
const { header, form } = contact2Data;
7+
8+
return (
9+
<section className="bg-primary-50 dark:bg-slate-800" id="contactTwo">
10+
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:px-8 lg:py-20">
11+
{header && <HeaderWidget header={header} titleClassname="text-3xl sm:text-5xl" />}
12+
<div className="flex items-stretch justify-center">
13+
<Form {...form} btnPosition="right" />
14+
</div>
15+
</div>
16+
</section>
17+
);
18+
};
19+
20+
export default Contact2;

src/components/widgets/Content.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { FC } from 'react';
21
import Image from 'next/image';
32
import { IconCheck } from '@tabler/icons-react';
43

54
import { ContentProps } from '~/shared/types';
65
import HeaderWidget from '../common/HeaderWidget';
76

8-
const Content: FC<ContentProps> = ({ header, content, items, image, isReversed, isAfterContent }) => (
7+
const Content = ({ header, content, items, image, isReversed, isAfterContent }: ContentProps) => (
98
<section className="bg-primary-50 dark:bg-slate-800">
109
<div
1110
className={`mx-auto max-w-6xl px-4 sm:px-6 lg:px-8 ${isAfterContent ? 'pt-1 pb-16 md:pb-20' : 'py-16 md:py-20'}`}

src/components/widgets/Features.tsx

+21-25
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,29 @@
1-
import { featuresData } from '~/shared/data';
1+
import { FeaturesProps } from '~/shared/types';
22
import HeaderWidget from '../common/HeaderWidget';
33

4-
const Features = () => {
5-
const { header, items } = featuresData;
6-
7-
return (
8-
<section className="scroll-mt-16" id="features">
9-
<div className="mx-auto max-w-6xl px-4 py-16 lg:px-8 lg:py-20">
10-
{header && <HeaderWidget header={header} titleClassname="text-4xl md:text-5xl" />}
11-
<div className="mx-auto grid space-y-6 md:grid-cols-2 md:space-y-0">
12-
{items.map(({ title, description, icon: Icon }, index) => (
13-
<div key={`item-feature-${index}`} className="space-y-8 sm:px-8">
14-
<div className="flex md:max-w-md">
15-
<div className="mb-4 mr-4">
16-
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary-500 dark:bg-primary-700">
17-
{Icon && <Icon className="icon-light h-6 w-6 text-white" />}
18-
</div>
19-
</div>
20-
<div className="mb-0 md:mb-8">
21-
<h3 className="mb-3 text-xl font-bold">{title}</h3>
22-
<p className="text-gray-600 dark:text-slate-400">{description}</p>
4+
const Features = ({ header, items }: FeaturesProps) => (
5+
<section className="scroll-mt-16" id="features">
6+
<div className="mx-auto max-w-6xl px-4 py-16 lg:px-8 lg:py-20">
7+
{header && <HeaderWidget header={header} titleClassname="text-4xl md:text-5xl" />}
8+
<div className="mx-auto grid space-y-6 md:grid-cols-2 md:space-y-0">
9+
{items.map(({ title, description, icon: Icon }, index) => (
10+
<div key={`item-feature-${index}`} className="space-y-8 sm:px-8">
11+
<div className="flex md:max-w-md">
12+
<div className="mb-4 mr-4">
13+
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary-500 dark:bg-primary-700">
14+
{Icon && <Icon className="icon-light h-6 w-6 text-white" />}
2315
</div>
2416
</div>
17+
<div className="mb-0 md:mb-8">
18+
<h3 className="mb-3 text-xl font-bold">{title}</h3>
19+
<p className="text-gray-600 dark:text-slate-400">{description}</p>
20+
</div>
2521
</div>
26-
))}
27-
</div>
22+
</div>
23+
))}
2824
</div>
29-
</section>
30-
);
31-
};
25+
</div>
26+
</section>
27+
);
3228

3329
export default Features;

src/components/widgets/Features2.tsx

+38-25
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,44 @@
1-
import { featuresData } from '~/shared/data';
1+
import { Fragment } from 'react';
2+
import { FeaturesProps } from '~/shared/types';
23
import HeaderWidget from '../common/HeaderWidget';
34

4-
const Features2 = () => {
5-
const { header, items } = featuresData;
6-
7-
return (
8-
<section className="relative py-16 lg:py-20" id="features2">
9-
<div className="pointer-events-none absolute inset-0 mb-36 bg-primary-50 dark:bg-slate-800"></div>
10-
<div className="relative mx-auto -mb-12 max-w-6xl px-4 sm:px-6">
11-
{header && <HeaderWidget header={header} titleClassname="text-4xl md:text-5xl" />}
12-
<div className={`my-12 grid items-stretch gap-6 dark:text-white sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3`}>
13-
{items.map(({ title, description, icon: Icon }, index) => (
14-
<div
15-
key={`item-feature2-${index}`}
16-
className="relative flex flex-col rounded border border-transparent bg-white p-6 shadow-lg transition hover:shadow-md dark:border-slate-800 dark:bg-slate-900"
17-
>
18-
<div className="flex items-center">
19-
{Icon && <Icon className="h-10 w-10" />}
20-
<div className="ml-4 text-xl font-bold">{title}</div>
5+
const Features2 = ({ header, items }: FeaturesProps) => (
6+
<section className="relative py-16 lg:py-20" id="features2">
7+
<div className="pointer-events-none absolute inset-0 mb-36 bg-primary-50 dark:bg-slate-800"></div>
8+
<div className="relative mx-auto -mb-12 max-w-6xl px-4 sm:px-6">
9+
{header && <HeaderWidget header={header} titleClassname="text-4xl md:text-5xl" />}
10+
<div
11+
className={`my-12 ${
12+
items.length > 2 ? 'grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3' : 'flex justify-center'
13+
} items-stretch gap-6`}
14+
>
15+
{items.map(({ title, description, icon: Icon, link }, index) => (
16+
<Fragment key={`item-feature2-${index}`}>
17+
{link ? (
18+
<a
19+
href={link.href}
20+
className="relative flex min-w-[22em] flex-col rounded border border-transparent bg-white p-6 shadow-lg transition hover:shadow-md dark:border-slate-800 dark:bg-slate-900 dark:text-white dark:shadow-[0_4px_10px_4px_rgba(30,41,59,0.3)]"
21+
>
22+
<div className="flex items-center">
23+
{Icon && <Icon className="h-10 w-10" />}
24+
<div className="ml-4 text-xl font-bold">{title}</div>
25+
</div>
26+
{description && <p className="text-md mt-4 text-gray-500 dark:text-gray-400">{description}</p>}
27+
</a>
28+
) : (
29+
<div className="relative flex min-w-[22em] flex-col rounded border border-transparent bg-white p-6 shadow-lg transition hover:shadow-md dark:border-slate-800 dark:bg-slate-900">
30+
<div className="flex items-center">
31+
{Icon && <Icon className="h-10 w-10" />}
32+
<div className="ml-4 text-xl font-bold">{title}</div>
33+
</div>
34+
{description && <p className="text-md mt-4 text-gray-500 dark:text-gray-400">{description}</p>}
2135
</div>
22-
{description && <p className="text-md mt-4 text-gray-500 dark:text-gray-400">{description}</p>}
23-
</div>
24-
))}
25-
</div>
36+
)}
37+
</Fragment>
38+
))}
2639
</div>
27-
</section>
28-
);
29-
};
40+
</div>
41+
</section>
42+
);
3043

3144
export default Features2;

0 commit comments

Comments
 (0)