Skip to content

Commit 45c028a

Browse files
committed
Backend Post API is integrated
1 parent 3627715 commit 45c028a

File tree

3 files changed

+232
-133
lines changed

3 files changed

+232
-133
lines changed

src/components/HireMeModal.jsx

Lines changed: 224 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,235 @@
11
import { motion } from 'framer-motion';
22
import { FiX } from 'react-icons/fi';
33
import Button from './reusable/Button';
4+
import { useState } from 'react';
5+
import toastr from 'toastr';
6+
import 'toastr/build/toastr.min.css';
47

58
const selectOptions = [
6-
'Web Application',
7-
'Mobile Application',
8-
'UI/UX Design',
9-
'Branding',
9+
'Web Application',
10+
'Mobile Application',
11+
'Desktop Application'
1012
];
1113

1214
const HireMeModal = ({ onClose, onRequest }) => {
13-
return (
14-
<motion.div
15-
initial={{ opacity: 0 }}
16-
animate={{ opacity: 1 }}
17-
exit={{ opacity: 0 }}
18-
className="font-general-medium fixed inset-0 z-30 transition-all duration-500"
19-
>
20-
{/* Modal Backdrop */}
21-
<div className="bg-filter bg-black bg-opacity-50 fixed inset-0 w-full h-full z-20"></div>
22-
23-
{/* Modal Content */}
24-
<main className="flex flex-col items-center justify-center h-full w-full">
25-
<div className="modal-wrapper flex items-center z-30">
26-
<div className="modal max-w-md mx-5 xl:max-w-xl lg:max-w-xl md:max-w-xl bg-secondary-light dark:bg-primary-dark max-h-screen shadow-lg flex-row rounded-lg relative">
27-
<div className="modal-header flex justify-between gap-10 p-5 border-b border-ternary-light dark:border-ternary-dark">
28-
<h5 className=" text-primary-dark dark:text-primary-light text-xl">
29-
What project are you looking for?
30-
</h5>
31-
<button
32-
onClick={onClose}
33-
className="px-4 font-bold text-primary-dark dark:text-primary-light"
34-
>
35-
<FiX className="text-3xl" />
36-
</button>
37-
</div>
38-
<div className="modal-body p-5 w-full h-full">
39-
<form
40-
onSubmit={(e) => {
41-
e.preventDefault();
42-
}}
43-
className="max-w-xl m-4 text-left"
44-
>
45-
<div className="">
46-
<input
47-
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
48-
id="name"
49-
name="name"
50-
type="text"
51-
required=""
52-
placeholder="Name"
53-
aria-label="Name"
54-
/>
55-
</div>
56-
<div className="mt-6">
57-
<input
58-
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
59-
id="email"
60-
name="email"
61-
type="text"
62-
required=""
63-
placeholder="Email"
64-
aria-label="Email"
65-
/>
66-
</div>
67-
<div className="mt-6">
68-
<select
69-
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
70-
id="subject"
71-
name="subject"
72-
type="text"
73-
required=""
74-
aria-label="Project Category"
75-
>
76-
{selectOptions.map((option) => (
77-
<option
78-
className="text-normal sm:text-md"
79-
key={option}
80-
>
81-
{option}
82-
</option>
83-
))}
84-
</select>
85-
</div>
86-
87-
<div className="mt-6">
88-
<textarea
89-
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
90-
id="message"
91-
name="message"
92-
cols="14"
93-
rows="6"
94-
aria-label="Details"
95-
placeholder="Project description"
96-
></textarea>
97-
</div>
98-
99-
<div className="mt-6 pb-4 sm:pb-1">
100-
<span
101-
onClick={onClose}
102-
type="submit"
103-
className="px-4
104-
sm:px-6
105-
py-2
106-
sm:py-2.5
107-
text-white
108-
bg-indigo-500
109-
hover:bg-indigo-600
110-
rounded-md
111-
focus:ring-1 focus:ring-indigo-900 duration-500"
112-
aria-label="Submit Request"
113-
>
114-
<Button title="Send Request" />
115-
</span>
116-
</div>
117-
</form>
118-
</div>
119-
<div className="modal-footer mt-2 sm:mt-0 py-5 px-8 border0-t text-right">
120-
<span
121-
onClick={onClose}
122-
type="button"
123-
className="px-4
124-
sm:px-6
125-
py-2 bg-gray-600 text-primary-light hover:bg-ternary-dark dark:bg-gray-200 dark:text-secondary-dark dark:hover:bg-primary-light
126-
rounded-md
127-
focus:ring-1 focus:ring-indigo-900 duration-500"
128-
aria-label="Close Modal"
129-
>
130-
<Button title="Close" />
131-
</span>
132-
</div>
133-
</div>
134-
</div>
135-
</main>
136-
</motion.div>
137-
);
15+
const [formData, setFormData] = useState({
16+
name: '',
17+
email: '',
18+
subject: 'Web Application', // Default value
19+
message: '',
20+
});
21+
22+
const [errors, setErrors] = useState({});
23+
24+
// Validation function
25+
const validateForm = () => {
26+
const newErrors = {};
27+
28+
if (!formData.name.trim()) {
29+
newErrors.name = 'Name is required.';
30+
}
31+
32+
if (!formData.email.trim()) {
33+
newErrors.email = 'Email is required.';
34+
} else if (
35+
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(formData.email)
36+
) {
37+
newErrors.email = 'Invalid email address.';
38+
}
39+
40+
if (!formData.message.trim()) {
41+
newErrors.message = 'Project description is required.';
42+
}
43+
44+
return newErrors;
45+
};
46+
47+
const handleInputChange = (e) => {
48+
const { name, value } = e.target;
49+
setFormData((prevData) => ({
50+
...prevData,
51+
[name]: value,
52+
}));
53+
54+
// Clear the error for the field being updated
55+
if (errors[name]) {
56+
setErrors((prevErrors) => ({
57+
...prevErrors,
58+
[name]: '',
59+
}));
60+
}
61+
};
62+
63+
const onSubmit = async (e) => {
64+
e.preventDefault();
65+
66+
// Validate the form
67+
const formErrors = validateForm();
68+
69+
if (Object.keys(formErrors).length > 0) {
70+
setErrors(formErrors);
71+
return;
72+
}
73+
74+
// Send the form data to the API
75+
try {
76+
const response = await fetch('http://porfolio-backend-env.eba-gvwg8p74.us-east-1.elasticbeanstalk.com/contact', {
77+
method: 'POST',
78+
headers: {
79+
'Content-Type': 'application/json',
80+
},
81+
body: JSON.stringify(formData),
82+
});
83+
84+
const data = await response.json();
85+
if (data.success) {
86+
// Show success notification
87+
toastr.success(data.message);
88+
onRequest(data.message);
89+
90+
setTimeout(() => {
91+
onClose(); // Close the modal after showing the notification
92+
}, 1000);
93+
94+
// Reset form data
95+
setFormData({
96+
name: '',
97+
email: '',
98+
subject: 'Web Application',
99+
message: '',
100+
});
101+
setErrors({});
102+
} else {
103+
onRequest('Failed to send the request. Please try again.');
104+
}
105+
} catch (error) {
106+
onRequest('An error occurred. Please try again later.');
107+
}
108+
};
109+
110+
return (
111+
<motion.div
112+
initial={{ opacity: 0 }}
113+
animate={{ opacity: 1 }}
114+
exit={{ opacity: 0 }}
115+
className="font-general-medium fixed inset-0 z-30 transition-all duration-500"
116+
>
117+
{/* Modal Backdrop */}
118+
<div className="bg-filter bg-black bg-opacity-50 fixed inset-0 w-full h-full z-20"></div>
119+
120+
{/* Modal Content */}
121+
<main className="flex flex-col items-center justify-center h-full w-full">
122+
<div className="modal-wrapper flex items-center z-30">
123+
<div className="modal max-w-md mx-5 xl:max-w-xl lg:max-w-xl md:max-w-xl bg-secondary-light dark:bg-primary-dark max-h-screen shadow-lg flex-row rounded-lg relative">
124+
<div className="modal-header flex justify-between gap-10 p-5 border-b border-ternary-light dark:border-ternary-dark">
125+
<h5 className=" text-primary-dark dark:text-primary-light text-xl">
126+
What project are you looking for?
127+
</h5>
128+
<button
129+
onClick={onClose}
130+
className="px-4 font-bold text-primary-dark dark:text-primary-light"
131+
>
132+
<FiX className="text-3xl" />
133+
</button>
134+
</div>
135+
<div className="modal-body p-5 w-full h-full">
136+
<form
137+
onSubmit={onSubmit}
138+
className="max-w-xl m-4 text-left"
139+
>
140+
<div>
141+
<input
142+
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
143+
id="name"
144+
name="name"
145+
type="text"
146+
placeholder="Name"
147+
aria-label="Name"
148+
value={formData.name}
149+
onChange={handleInputChange}
150+
/>
151+
{errors.name && (
152+
<p className="text-red-500 text-sm mt-1">{errors.name}</p>
153+
)}
154+
</div>
155+
<div className="mt-6">
156+
<input
157+
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
158+
id="email"
159+
name="email"
160+
type="email"
161+
placeholder="Email"
162+
aria-label="Email"
163+
value={formData.email}
164+
onChange={handleInputChange}
165+
/>
166+
{errors.email && (
167+
<p className="text-red-500 text-sm mt-1">{errors.email}</p>
168+
)}
169+
</div>
170+
<div className="mt-6">
171+
<select
172+
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
173+
id="subject"
174+
name="subject"
175+
aria-label="Project Category"
176+
value={formData.subject}
177+
onChange={handleInputChange}
178+
>
179+
{selectOptions.map((option) => (
180+
<option
181+
className="text-normal sm:text-md"
182+
key={option}
183+
>
184+
{option}
185+
</option>
186+
))}
187+
</select>
188+
</div>
189+
190+
<div className="mt-6">
191+
<textarea
192+
className="w-full px-5 py-2 border dark:border-secondary-dark rounded-md text-md bg-secondary-light dark:bg-ternary-dark text-primary-dark dark:text-ternary-light"
193+
id="message"
194+
name="message"
195+
cols="14"
196+
rows="6"
197+
aria-label="Details"
198+
placeholder="Project description"
199+
value={formData.message}
200+
onChange={handleInputChange}
201+
></textarea>
202+
{errors.message && (
203+
<p className="text-red-500 text-sm mt-1">{errors.message}</p>
204+
)}
205+
</div>
206+
207+
<div className="mt-6 pb-4 sm:pb-1">
208+
<button
209+
type="submit"
210+
className="px-4 sm:px-6 py-2 sm:py-2.5 text-white bg-indigo-500 hover:bg-indigo-600 rounded-md focus:ring-1 focus:ring-indigo-900 duration-500"
211+
aria-label="Submit Request"
212+
>
213+
<Button title="Send Request" />
214+
</button>
215+
</div>
216+
</form>
217+
</div>
218+
<div className="modal-footer mt-2 sm:mt-0 py-5 px-8 border0-t text-right">
219+
<button
220+
onClick={onClose}
221+
type="button"
222+
className="px-4 sm:px-6 py-2 bg-gray-600 text-primary-light hover:bg-ternary-dark dark:bg-gray-200 dark:text-secondary-dark dark:hover:bg-primary-light rounded-md focus:ring-1 focus:ring-indigo-900 duration-500"
223+
aria-label="Close Modal"
224+
>
225+
<Button title="Close" />
226+
</button>
227+
</div>
228+
</div>
229+
</div>
230+
</main>
231+
</motion.div>
232+
);
138233
};
139234

140235
export default HireMeModal;

src/components/contact/ContactForm.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Button from '../reusable/Button';
22
import FormInput from '../reusable/FormInput';
33
import { useState } from 'react';
4-
import toastr from 'toastr'; // Make sure toastr is installed and imported
5-
import 'toastr/build/toastr.min.css'; // Include toastr CSS
4+
import toastr from 'toastr';
5+
import 'toastr/build/toastr.min.css';
66

77
const ContactForm = () => {
88
const [formData, setFormData] = useState({
@@ -73,7 +73,7 @@ const ContactForm = () => {
7373

7474
try {
7575
// Make the API call
76-
const response = await fetch('http://localhost:8081/contact', {
76+
const response = await fetch('http://porfolio-backend-env.eba-gvwg8p74.us-east-1.elasticbeanstalk.com/contact', {
7777
method: 'POST',
7878
headers: {
7979
'Content-Type': 'application/json',
@@ -85,7 +85,7 @@ const ContactForm = () => {
8585

8686
if (data.success) {
8787
toastr.success(data.message); // Display toastr notification
88-
// Clear form values after successful submission
88+
8989
setFormData({
9090
name: '',
9191
email: '',

0 commit comments

Comments
 (0)