11"use client" ;
22
3- import { useState , useEffect } from "react" ;
3+ import { useState , useEffect , ChangeEvent , FormEvent , JSX } from "react" ;
44import { motion , AnimatePresence } from "framer-motion" ;
55import emailjs from "emailjs-com" ;
66import {
@@ -9,125 +9,181 @@ import {
99 FaCommentDots ,
1010 FaTimes ,
1111 FaCheckCircle ,
12- FaPaperPlane
12+ FaExclamationCircle ,
13+ FaPaperPlane ,
14+ FaSpinner
1315} from "react-icons/fa" ;
1416
15- export default function ContactForm ( ) {
16- const [ form , setForm ] = useState ( { name : "" , number : "" , message : "" } ) ;
17- const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
18- const [ showModal , setShowModal ] = useState ( false ) ;
17+ // ============================================================
18+ // TYPES
19+ // ============================================================
1920
20- const handleChange = ( e : React . ChangeEvent < HTMLInputElement | HTMLTextAreaElement > ) => {
21- setForm ( { ...form , [ e . target . name ] : e . target . value } ) ;
21+ interface FormData {
22+ name : string ;
23+ number : string ;
24+ message : string ;
25+ }
26+
27+ // ============================================================
28+ // CONTACT FORM COMPONENT
29+ // ============================================================
30+
31+ export default function ContactForm ( ) : JSX . Element {
32+ const [ form , setForm ] = useState < FormData > ( {
33+ name : "" ,
34+ number : "" ,
35+ message : ""
36+ } ) ;
37+ const [ isSubmitting , setIsSubmitting ] = useState < boolean > ( false ) ;
38+ const [ modalStatus , setModalStatus ] = useState < "idle" | "success" | "error" > ( "idle" ) ;
39+ const [ showModal , setShowModal ] = useState < boolean > ( false ) ;
40+
41+ // ============================================================
42+ // HANDLERS
43+ // ============================================================
44+
45+ const handleChange = ( e : ChangeEvent < HTMLInputElement | HTMLTextAreaElement > ) : void => {
46+ const { name, value } = e . target ;
47+ setForm ( ( prev ) => ( { ...prev , [ name ] : value } ) ) ;
2248 } ;
2349
24- const sendEmail = async ( e : React . FormEvent ) => {
50+ const sendEmail = async ( e : FormEvent < HTMLFormElement > ) : Promise < void > => {
2551 e . preventDefault ( ) ;
2652 setIsSubmitting ( true ) ;
53+ setModalStatus ( "idle" ) ;
2754
2855 try {
2956 await emailjs . send (
30- "service_97usflj " ,
57+ "service_np5zft2 " ,
3158 "template_m9immuc" ,
32- form ,
59+ { ... form } ,
3360 "q1s3x3DSUxpAVErUh"
3461 ) ;
3562 setForm ( { name : "" , number : "" , message : "" } ) ;
63+ setModalStatus ( "success" ) ;
3664 setShowModal ( true ) ;
3765 } catch ( error ) {
38- console . error ( error ) ;
39- alert ( "Failed to send message" ) ;
66+ console . error ( "EmailJS Error:" , error ) ;
67+ setModalStatus ( "error" ) ;
68+ setShowModal ( true ) ;
4069 } finally {
4170 setIsSubmitting ( false ) ;
4271 }
4372 } ;
4473
74+ const closeModal = ( ) : void => {
75+ setShowModal ( false ) ;
76+ setTimeout ( ( ) => setModalStatus ( "idle" ) , 300 ) ;
77+ } ;
78+
79+ // ============================================================
80+ // EFFECTS
81+ // ============================================================
82+
4583 useEffect ( ( ) => {
46- const handleEsc = ( e : KeyboardEvent ) => {
47- if ( e . key === "Escape" ) setShowModal ( false ) ;
84+ const handleEsc = ( e : KeyboardEvent ) : void => {
85+ if ( e . key === "Escape" ) closeModal ( ) ;
4886 } ;
4987 document . addEventListener ( "keydown" , handleEsc ) ;
5088 return ( ) => document . removeEventListener ( "keydown" , handleEsc ) ;
5189 } , [ ] ) ;
5290
91+ // ============================================================
92+ // RENDER
93+ // ============================================================
94+
5395 return (
54- < section id = "contact" className = "min-h-screen flex items-center justify-center px-4 py-20 bg-black" >
55-
56- { /* Simple Glow */ }
96+ < section
97+ id = "contact"
98+ className = "min-h-screen flex flex-col justify-center items-center px-4 py-20 bg-black relative overflow-hidden"
99+ >
100+ { /* Background Glow */ }
57101 < div className = "absolute inset-0" >
58- < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[500px] bg-white/5 rounded-full blur-3xl" />
102+ < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[500px] bg-orange-400/5 rounded-full blur-3xl" />
103+ < div className = "absolute bottom-20 right-1/4 w-[300px] h-[300px] bg-orange-400/5 rounded-full blur-3xl" />
59104 </ div >
60105
61106 < div className = "max-w-2xl w-full relative z-10" >
107+ { /* ==========================================================
108+ HEADER
109+ ========================================================== */ }
62110
63- { /* Header */ }
64111 < motion . div
65112 initial = { { opacity : 0 , y : 20 } }
66113 animate = { { opacity : 1 , y : 0 } }
67114 transition = { { duration : 0.5 } }
68115 className = "text-center mb-10"
69116 >
117+ < div className = "inline-flex items-center gap-2 px-4 py-2 bg-orange-400/10 rounded-full border border-orange-400/20 mb-4" >
118+ < FaPaperPlane className = "text-orange-300 text-xs" />
119+ < span className = "text-[10px] font-medium text-orange-300 tracking-wider uppercase" >
120+ Get in Touch
121+ </ span >
122+ </ div >
123+
70124 < h2 className = "text-4xl sm:text-5xl font-light text-white tracking-tight" >
71125 Contact
72126 </ h2 >
73- < div className = "w-12 h-0.5 bg-white/20 mx-auto mt-3" />
127+ < div className = "w-12 h-0.5 bg-orange-400/30 mx-auto mt-3" />
74128 < p className = "text-gray-500 text-sm mt-4" >
75- Lets work together
129+ Let's work together
76130 </ p >
77131 </ motion . div >
78132
79- { /* Form */ }
133+ { /* ==========================================================
134+ FORM
135+ ========================================================== */ }
136+
80137 < motion . div
81138 initial = { { opacity : 0 , y : 30 } }
82139 animate = { { opacity : 1 , y : 0 } }
83140 transition = { { duration : 0.5 , delay : 0.2 } }
84- className = "bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-6 sm:p-8"
141+ className = "bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl p-6 sm:p-8 hover:border-orange-400/30 transition-all duration-300 "
85142 >
86143 < form onSubmit = { sendEmail } className = "space-y-4" >
87-
88- { /* Name + Phone */ }
89- < div className = "grid grid-cols-1 sm:grid-cols-2 gap-4" >
90- < div className = "relative" >
91- < FaUser className = "absolute left-4 top-1/2 -translate-y-1/2 text-gray-600" size = { 15 } />
144+ { /* Name + Phone - زیر هم */ }
145+ < div className = "grid grid-cols-1 gap-4" >
146+ < div className = "relative group" >
147+ < FaUser className = "absolute left-4 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-orange-300 transition-colors duration-300" size = { 15 } />
92148 < input
93149 type = "text"
94150 name = "name"
95151 placeholder = "Name"
96152 value = { form . name }
97153 onChange = { handleChange }
98- className = "w-full pl-11 pr-4 py-3.5 bg-black/50 border border-white/10 rounded-xl text-white placeholder-gray-600 focus:outline-none focus:border-white/30 transition-all duration-300"
154+ className = "w-full pl-11 pr-4 py-3.5 bg-black/50 border border-white/10 rounded-xl text-white placeholder-gray-600 focus:outline-none focus:border-orange-400/50 transition-all duration-300"
99155 required
100156 />
101157 </ div >
102- < div className = "relative" >
103- < FaPhone className = "absolute left-4 top-1/2 -translate-y-1/2 text-gray-600" size = { 15 } />
158+ < div className = "relative group " >
159+ < FaPhone className = "absolute left-4 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-orange-300 transition-colors duration-300 " size = { 15 } />
104160 < input
105161 type = "tel"
106162 name = "number"
107163 placeholder = "Phone"
108164 value = { form . number }
109165 onChange = { handleChange }
110- className = "w-full pl-11 pr-4 py-3.5 bg-black/50 border border-white/10 rounded-xl text-white placeholder-gray-600 focus:outline-none focus:border-white/30 transition-all duration-300"
166+ className = "w-full pl-11 pr-4 py-3.5 bg-black/50 border border-white/10 rounded-xl text-white placeholder-gray-600 focus:outline-none focus:border-orange-400/50 transition-all duration-300"
111167 required
112168 />
113169 </ div >
114170 </ div >
115171
116172 { /* Message */ }
117- < div className = "relative" >
118- < FaCommentDots className = "absolute left-4 top-4 text-gray-600" size = { 15 } />
173+ < div className = "relative group " >
174+ < FaCommentDots className = "absolute left-4 top-4 text-gray-600 group-focus-within:text-orange-300 transition-colors duration-300 " size = { 15 } />
119175 < textarea
120176 name = "message"
121177 placeholder = "Message"
122178 value = { form . message }
123179 onChange = { handleChange }
124180 rows = { 4 }
125- className = "w-full pl-11 pr-4 py-3.5 bg-black/50 border border-white/10 rounded-xl text-white placeholder-gray-600 focus:outline-none focus:border-white/30 transition-all duration-300 resize-none"
181+ className = "w-full pl-11 pr-4 py-3.5 bg-black/50 border border-white/10 rounded-xl text-white placeholder-gray-600 focus:outline-none focus:border-orange-400/50 transition-all duration-300 resize-none"
126182 required
127183 />
128184 </ div >
129185
130- { /* Submit */ }
186+ { /* Submit Button */ }
131187 < motion . button
132188 type = "submit"
133189 disabled = { isSubmitting }
@@ -136,60 +192,123 @@ export default function ContactForm() {
136192 className = { `w-full py-3.5 text-sm font-medium tracking-wider rounded-xl transition-all duration-300 ${
137193 isSubmitting
138194 ? "bg-white/5 text-gray-500 cursor-not-allowed"
139- : "bg-white text-black hover:bg-white/90 "
195+ : "bg-orange-400/20 text-orange-300 border border-orange-400/30 hover:bg-orange-400/30 shadow-lg shadow-orange-400/10 "
140196 } `}
141197 >
142198 { isSubmitting ? (
143199 < span className = "flex items-center justify-center gap-2" >
144- < div className = "w-4 h-4 border-2 border-black/30 border-t-black rounded-full animate-spin" />
145- Sending
200+ < FaSpinner className = "animate-spin" size = { 16 } />
201+ Sending...
146202 </ span >
147203 ) : (
148204 < span className = "flex items-center justify-center gap-2" >
149- Send
205+ Send Message
150206 < FaPaperPlane size = { 13 } />
151207 </ span >
152208 ) }
153209 </ motion . button >
154210 </ form >
155211 </ motion . div >
156212
157- { /* Modal */ }
213+ { /* ==========================================================
214+ SUCCESS/ERROR MODAL - خفن و شیک
215+ ========================================================== */ }
216+
158217 < AnimatePresence >
159218 { showModal && (
160219 < motion . div
161220 initial = { { opacity : 0 } }
162221 animate = { { opacity : 1 } }
163222 exit = { { opacity : 0 } }
164223 className = "fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm"
165- onClick = { ( ) => setShowModal ( false ) }
166224 >
167225 < motion . div
168- initial = { { scale : 0.9 , opacity : 0 } }
169- animate = { { scale : 1 , opacity : 1 } }
170- exit = { { scale : 0.9 , opacity : 0 } }
171- className = "relative max-w-sm w-full bg-[#111] border border-white/10 rounded-2xl p-8 text-center"
172- onClick = { ( e ) => e . stopPropagation ( ) }
226+ initial = { { scale : 0.8 , opacity : 0 , y : 30 } }
227+ animate = { { scale : 1 , opacity : 1 , y : 0 } }
228+ exit = { { scale : 0.8 , opacity : 0 , y : 30 } }
229+ transition = { { type : "spring" , stiffness : 300 , damping : 25 } }
230+ className = "relative max-w-md w-full bg-gradient-to-br from-[#1a1a1a] to-[#0d0d0d] border rounded-2xl p-8 text-center shadow-2xl"
231+ style = { {
232+ borderColor : modalStatus === "success"
233+ ? "rgba(74, 222, 128, 0.3)"
234+ : "rgba(248, 113, 113, 0.3)"
235+ } }
173236 >
237+ { /* Close Button - فقط با کلیک روی این دکمه بسته میشه */ }
174238 < button
175- onClick = { ( ) => setShowModal ( false ) }
176- className = "absolute top-4 right-4 text-gray-500 hover:text-white transition-colors"
239+ onClick = { closeModal }
240+ className = "absolute top-4 right-4 text-gray-500 hover:text-white transition-colors p-2 hover:bg-white/5 rounded-full "
177241 >
178242 < FaTimes size = { 18 } />
179243 </ button >
180244
181- < div className = "w-16 h-16 mx-auto mb-4 rounded-full bg-white/5 border border-white/10 flex items-center justify-center" >
182- < FaCheckCircle className = "text-white/60 text-2xl" />
183- </ div >
245+ { /* Icon */ }
246+ < motion . div
247+ initial = { { scale : 0 } }
248+ animate = { { scale : 1 } }
249+ transition = { { delay : 0.2 , type : "spring" , stiffness : 300 } }
250+ className = "w-20 h-20 mx-auto mb-4 rounded-full flex items-center justify-center"
251+ style = { {
252+ background : modalStatus === "success"
253+ ? "rgba(74, 222, 128, 0.15)"
254+ : "rgba(248, 113, 113, 0.15)" ,
255+ border : `2px solid ${
256+ modalStatus === "success"
257+ ? "rgba(74, 222, 128, 0.3)"
258+ : "rgba(248, 113, 113, 0.3)"
259+ } `
260+ } }
261+ >
262+ { modalStatus === "success" ? (
263+ < FaCheckCircle className = "text-4xl text-green-400" />
264+ ) : (
265+ < FaExclamationCircle className = "text-4xl text-red-400" />
266+ ) }
267+ </ motion . div >
184268
185- < h3 className = "text-xl font-light text-white mb-2" > Sent!</ h3 >
269+ { /* Title */ }
270+ < h3
271+ className = "text-2xl font-bold mb-2"
272+ style = { {
273+ color : modalStatus === "success" ? "#4ADE80" : "#F87171"
274+ } }
275+ >
276+ { modalStatus === "success" ? "Message Sent! 🎉" : "Oops! Something went wrong" }
277+ </ h3 >
186278
187- < button
188- onClick = { ( ) => setShowModal ( false ) }
189- className = "mt-6 w-full py-2.5 bg-white text-black text-sm font-medium rounded-xl hover:bg-white/90 transition-all duration-300"
279+ { /* Message */ }
280+ < p className = "text-gray-400 text-sm leading-relaxed mb-6" >
281+ { modalStatus === "success"
282+ ? "Your message has been sent successfully. I'll get back to you as soon as possible."
283+ : "We couldn't send your message. Please check your connection and try again, or contact me directly via email."
284+ }
285+ </ p >
286+
287+ { /* Button - فقط با کلیک روی این دکمه بسته میشه */ }
288+ < motion . button
289+ onClick = { closeModal }
290+ whileHover = { { scale : 1.02 } }
291+ whileTap = { { scale : 0.98 } }
292+ className = "w-full py-3 text-sm font-medium rounded-xl transition-all duration-300"
293+ style = { {
294+ background : modalStatus === "success"
295+ ? "linear-gradient(135deg, #4ADE80, #22C55E)"
296+ : "linear-gradient(135deg, #F87171, #EF4444)" ,
297+ color : "#0A0A0A"
298+ } }
190299 >
191- Done
192- </ button >
300+ { modalStatus === "success" ? "Got it! ✅" : "Try Again 🔄" }
301+ </ motion . button >
302+
303+ { /* Subtle glow effect */ }
304+ < div
305+ className = "absolute -inset-1 rounded-2xl blur-3xl opacity-20 -z-10"
306+ style = { {
307+ background : modalStatus === "success"
308+ ? "radial-gradient(circle, #4ADE80, transparent 70%)"
309+ : "radial-gradient(circle, #F87171, transparent 70%)"
310+ } }
311+ />
193312 </ motion . div >
194313 </ motion . div >
195314 ) }
0 commit comments