Skip to content

Commit b3b9714

Browse files
committed
ft(#630): Implement the contact us page
1 parent 4e1afad commit b3b9714

File tree

4 files changed

+218
-1
lines changed

4 files changed

+218
-1
lines changed

src/Mutations/contactUs.mutation.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { gql } from '@apollo/client';
2+
3+
export const SEND_MESSAGE_MUTATION = gql`
4+
mutation sendMessage(
5+
$name: String!
6+
$email: String!
7+
$phone: String
8+
$message: String!
9+
) {
10+
sendMessage(name: $name, email: $email, phone: $phone, message: $message)
11+
}
12+
`;

src/components/Footer.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ function Footer({ styles }: any) {
6464
<h3 className="font-bold mb-2">{t('Dev Pulse')}</h3>
6565
<ul>
6666
<li className="mb-1">{t('About us')}</li>
67-
<li className="mb-1">{t('Contact us')}</li>
67+
<li className="mb-1">
68+
<Link to="/contact-us">{t('Contact us')} </Link>
69+
</li>
6870
</ul>
6971
</div>
7072
<div className="w-full sm:w-1/2 md:w-1/4 p-4 text-center lg:text-left">

src/containers/Routes.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const About = React.lazy(() => import('../pages/Comingsoon'));
2525
const Community = React.lazy(() => import('../pages/Community'));
2626
/* istanbul ignore next */
2727
const Product = React.lazy(() => import('../pages/Comingsoon'));
28+
const ContactUs = React.lazy(() => import('../pages/ContactUs'));
2829
/* istanbul ignore next */
2930
const SignupOrgDocs = React.lazy(
3031
() => import('../components/Docs/SignupOrgDocs'),
@@ -162,6 +163,14 @@ function MainRoutes() {
162163
</Suspense>
163164
}
164165
/>
166+
<Route
167+
path="/contact-us"
168+
element={
169+
<Suspense fallback={<Skeleton />}>
170+
<ContactUs />
171+
</Suspense>
172+
}
173+
/>
165174
<Route path="/docs/org-signup" element={<SignupOrgDocs />} />
166175
<Route path="/docs/org-signin" element={<SigninOrgDocs />} />
167176
<Route

src/pages/ContactUs.tsx

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import React, { useState } from 'react';
2+
import { toast } from 'react-toastify';
3+
import { useMutation } from '@apollo/client';
4+
import { SEND_MESSAGE_MUTATION } from '../Mutations/contactUs.mutation';
5+
6+
interface FormData {
7+
name: string;
8+
email: string;
9+
phone: string;
10+
message: string;
11+
}
12+
13+
export default function ContactUs() {
14+
const [formData, setFormData] = useState<FormData>({
15+
name: '',
16+
email: '',
17+
phone: '',
18+
message: '',
19+
});
20+
const [sendMessage, { loading, error }] = useMutation(SEND_MESSAGE_MUTATION);
21+
22+
const [errors, setErrors] = useState<{ [key: string]: string }>({});
23+
24+
// Handle form input changes
25+
const handleChange = (
26+
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
27+
) => {
28+
const { name, value } = e.target;
29+
setFormData({
30+
...formData,
31+
[name]: value,
32+
});
33+
34+
if (errors[name]) {
35+
setErrors({
36+
...errors,
37+
[name]: '',
38+
});
39+
}
40+
};
41+
42+
// Validation function
43+
const validateForm = () => {
44+
const newErrors: { [key: string]: string } = {};
45+
46+
if (!formData.name) {
47+
newErrors.name = 'Name is required';
48+
}
49+
50+
// Email validation with regex
51+
const emailPattern = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
52+
if (!formData.email) {
53+
newErrors.email = 'Email is required';
54+
} else if (!emailPattern.test(formData.email)) {
55+
newErrors.email = 'Invalid email address';
56+
}
57+
58+
if (!formData.message) {
59+
newErrors.message = 'Message is required';
60+
}
61+
62+
setErrors(newErrors);
63+
return Object.keys(newErrors).length === 0;
64+
};
65+
66+
// Handle form submission
67+
const handleSubmit = async (e: React.FormEvent) => {
68+
e.preventDefault();
69+
70+
if (!validateForm()) {
71+
return;
72+
}
73+
74+
try {
75+
const { data } = await sendMessage({
76+
variables: {
77+
name: formData.name,
78+
email: formData.email,
79+
phone: formData.phone,
80+
message: formData.message,
81+
},
82+
});
83+
toast.success('Your message has been sent successfully!');
84+
setFormData({ name: '', email: '', phone: '', message: '' });
85+
} catch (err) {
86+
toast.error('Error submitting the form, Try again');
87+
}
88+
};
89+
90+
return (
91+
<div className="flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800">
92+
<div className="w-full max-w-lg p-6 rounded-lg shadow-none xmd:shadow-lg dark:bg-gray-800 xmd:dark:bg-gray-900 bg-gray-100 xmd:bg-white">
93+
<h2 className="text-center text-3xl font-bold text-gray-900 dark:text-white mb-4">
94+
Contact Us
95+
</h2>
96+
<p className="text-center text-gray-600 dark:text-gray-300 mb-6">
97+
We would love to hear from you. Please fill out the form below to get
98+
in touch.
99+
</p>
100+
101+
<form onSubmit={handleSubmit} className="space-y-4">
102+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
103+
<div className="flex flex-col">
104+
<label
105+
htmlFor="name"
106+
className="text-gray-700 dark:text-gray-300 font-semibold"
107+
>
108+
Name <span className="text-red-500">*</span>
109+
</label>
110+
<input
111+
type="text"
112+
id="name"
113+
name="name"
114+
value={formData.name}
115+
onChange={handleChange}
116+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-black dark:border-gray-600"
117+
/>
118+
{errors.name && (
119+
<small className="text-red-600">{errors.name}</small>
120+
)}
121+
</div>
122+
123+
<div className="flex flex-col">
124+
<label
125+
htmlFor="email"
126+
className="text-gray-700 dark:text-gray-300 font-semibold"
127+
>
128+
Email <span className="text-red-500">*</span>
129+
</label>
130+
<input
131+
type="text"
132+
id="email"
133+
name="email"
134+
value={formData.email}
135+
onChange={handleChange}
136+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-black dark:border-gray-600"
137+
/>
138+
{errors.email && (
139+
<small className="text-red-600">{errors.email}</small>
140+
)}
141+
</div>
142+
</div>
143+
144+
<div className="flex flex-col">
145+
<label
146+
htmlFor="phone"
147+
className="text-gray-700 dark:text-gray-300 font-semibold"
148+
>
149+
Phone Number
150+
</label>
151+
<input
152+
type="tel"
153+
id="phone"
154+
name="phone"
155+
value={formData.phone}
156+
onChange={handleChange}
157+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-black dark:border-gray-600"
158+
/>
159+
</div>
160+
161+
<div className="flex flex-col">
162+
<label
163+
htmlFor="message"
164+
className="text-gray-700 dark:text-gray-300 font-semibold"
165+
>
166+
Message <span className="text-red-500">*</span>
167+
</label>
168+
<textarea
169+
id="message"
170+
name="message"
171+
value={formData.message}
172+
onChange={handleChange}
173+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-black dark:border-gray-600"
174+
rows={4}
175+
/>
176+
{errors.message && (
177+
<small className="text-red-600">{errors.message}</small>
178+
)}
179+
</div>
180+
181+
<div className="text-center">
182+
<button
183+
type="submit"
184+
disabled={loading}
185+
className="w-full mt-4 py-2 bg-primary text-white font-semibold rounded-lg focus:outline-none"
186+
>
187+
{loading ? 'Submitting...' : 'Submit'}
188+
</button>
189+
</div>
190+
</form>
191+
</div>
192+
</div>
193+
);
194+
}

0 commit comments

Comments
 (0)