Skip to content

Commit 9310fa4

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

File tree

4 files changed

+220
-1
lines changed

4 files changed

+220
-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

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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+
// eslint-disable-next-line no-console
87+
console.log(err)
88+
toast.error('Error submitting the form, Try again');
89+
}
90+
};
91+
92+
return (
93+
<div className="flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800">
94+
<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">
95+
<h2 className="text-center text-3xl font-bold text-gray-900 dark:text-white mb-4">
96+
Contact Us
97+
</h2>
98+
<p className="text-center text-gray-600 dark:text-gray-300 mb-6">
99+
We would love to hear from you. Please fill out the form below to get
100+
in touch.
101+
</p>
102+
103+
<form onSubmit={handleSubmit} className="space-y-4">
104+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
105+
<div className="flex flex-col">
106+
<label
107+
htmlFor="name"
108+
className="text-gray-700 dark:text-gray-300 font-semibold"
109+
>
110+
Name <span className="text-red-500">*</span>
111+
</label>
112+
<input
113+
type="text"
114+
id="name"
115+
name="name"
116+
value={formData.name}
117+
onChange={handleChange}
118+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-white dark:border-white dark:bg-gray-900"
119+
/>
120+
{errors.name && (
121+
<small className="text-red-600">{errors.name}</small>
122+
)}
123+
</div>
124+
125+
<div className="flex flex-col">
126+
<label
127+
htmlFor="email"
128+
className="text-gray-700 dark:text-gray-300 font-semibold"
129+
>
130+
Email <span className="text-red-500">*</span>
131+
</label>
132+
<input
133+
type="text"
134+
id="email"
135+
name="email"
136+
value={formData.email}
137+
onChange={handleChange}
138+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-white dark:border-white dark:bg-gray-900"
139+
/>
140+
{errors.email && (
141+
<small className="text-red-600">{errors.email}</small>
142+
)}
143+
</div>
144+
</div>
145+
146+
<div className="flex flex-col">
147+
<label
148+
htmlFor="phone"
149+
className="text-gray-700 dark:text-gray-300 font-semibold"
150+
>
151+
Phone Number
152+
</label>
153+
<input
154+
type="tel"
155+
id="phone"
156+
name="phone"
157+
value={formData.phone}
158+
onChange={handleChange}
159+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-white dark:border-white dark:bg-gray-900"
160+
/>
161+
</div>
162+
163+
<div className="flex flex-col">
164+
<label
165+
htmlFor="message"
166+
className="text-gray-700 dark:text-gray-300 font-semibold"
167+
>
168+
Message <span className="text-red-500">*</span>
169+
</label>
170+
<textarea
171+
id="message"
172+
name="message"
173+
value={formData.message}
174+
onChange={handleChange}
175+
className="mt-2 p-2 border rounded-md border-gray-300 dark:text-white dark:border-white dark:bg-gray-900"
176+
rows={4}
177+
/>
178+
{errors.message && (
179+
<small className="text-red-600">{errors.message}</small>
180+
)}
181+
</div>
182+
183+
<div className="text-center">
184+
<button
185+
type="submit"
186+
disabled={loading}
187+
className="w-full mt-4 py-2 bg-primary text-white font-semibold rounded-lg focus:outline-none"
188+
>
189+
{loading ? 'Submitting...' : 'Submit'}
190+
</button>
191+
</div>
192+
</form>
193+
</div>
194+
</div>
195+
);
196+
}

0 commit comments

Comments
 (0)