@@ -10,15 +10,85 @@ import { ThemeToggle } from "@/components/theme-toggle"
10
10
import { useState } from "react"
11
11
import { useSmoothScroll } from "@/hooks/use-smooth-scroll"
12
12
import Script from "next/script"
13
+ import { z } from "zod"
14
+ import { toast } from "sonner"
13
15
14
16
// Skill component for highlighting technologies
15
17
const Skill = ( { children } : { children : React . ReactNode } ) => (
16
18
< strong className = "text-primary" > { children } </ strong >
17
19
) ;
18
20
21
+ // Contact form schema for validation
22
+ const contactFormSchema = z . object ( {
23
+ name : z . string ( ) . min ( 2 , { message : "Name must be at least 2 characters" } ) ,
24
+ email : z . string ( ) . email ( { message : "Please enter a valid email address" } ) ,
25
+ message : z . string ( ) . min ( 10 , { message : "Message must be at least 10 characters" } )
26
+ } ) ;
27
+
28
+ type ContactFormValues = z . infer < typeof contactFormSchema > ;
29
+
19
30
export default function Home ( ) {
20
31
const [ mobileMenuOpen , setMobileMenuOpen ] = useState ( false )
21
32
const handleSmoothScroll = useSmoothScroll ( )
33
+ const [ isSubmitting , setIsSubmitting ] = useState ( false )
34
+ const [ errors , setErrors ] = useState < { [ key : string ] : string } > ( { } )
35
+
36
+ const handleSubmit = async ( e : React . FormEvent < HTMLFormElement > ) => {
37
+ e . preventDefault ( ) ;
38
+ setIsSubmitting ( true ) ;
39
+ setErrors ( { } ) ;
40
+
41
+ // Store a reference to the form element
42
+ const form = e . currentTarget ;
43
+
44
+ // Get form data
45
+ const formData = new FormData ( form ) ;
46
+ const data = {
47
+ name : formData . get ( 'name' ) as string ,
48
+ email : formData . get ( 'email' ) as string ,
49
+ message : formData . get ( 'message' ) as string
50
+ } ;
51
+
52
+ // Validate form data
53
+ const result = contactFormSchema . safeParse ( data ) ;
54
+
55
+ if ( ! result . success ) {
56
+ // Format and display validation errors
57
+ const formattedErrors : { [ key : string ] : string } = { } ;
58
+ result . error . issues . forEach ( issue => {
59
+ formattedErrors [ issue . path [ 0 ] as string ] = issue . message ;
60
+ } ) ;
61
+ setErrors ( formattedErrors ) ;
62
+ setIsSubmitting ( false ) ;
63
+ return ;
64
+ }
65
+
66
+ try {
67
+ // Send the form data to your API route
68
+ const response = await fetch ( '/api/contact' , {
69
+ method : 'POST' ,
70
+ headers : {
71
+ 'Content-Type' : 'application/json' ,
72
+ } ,
73
+ body : JSON . stringify ( data ) ,
74
+ } ) ;
75
+
76
+ const responseData = await response . json ( ) ;
77
+
78
+ if ( ! response . ok ) {
79
+ throw new Error ( responseData . error || 'Failed to send message' ) ;
80
+ }
81
+
82
+ // Reset the form using the stored reference
83
+ form . reset ( ) ;
84
+ toast . success ( 'Message sent successfully! I will get back to you soon.' ) ;
85
+ } catch ( error ) {
86
+ console . error ( 'Error submitting form:' , error ) ;
87
+ toast . error ( 'Failed to send message. Please try again later.' ) ;
88
+ } finally {
89
+ setIsSubmitting ( false ) ;
90
+ }
91
+ } ;
22
92
23
93
return (
24
94
< div className = "min-h-screen bg-background" >
@@ -188,7 +258,7 @@ export default function Home() {
188
258
</ div >
189
259
< div className = "flex-1 flex justify-center md:justify-end" >
190
260
< div className = "relative w-64 h-64 md:w-80 md:h-80 rounded-full overflow-hidden border-4 border-primary/20" >
191
- < Image src = "/placeholder.svg ?height=320& width = 320 " alt = "Profile" fill className = "object-cover" priority />
261
+ < Image src = "pfp2.png ?height=320& width = 320 " alt = "Profile" fill className = "object-cover" priority />
192
262
</ div >
193
263
</ div >
194
264
</ section >
@@ -547,40 +617,52 @@ export default function Home() {
547
617
</ div >
548
618
< Card className = "hover:shadow-md hover:shadow-primary/10 transition-shadow" >
549
619
< CardContent className = "p-6" >
550
- < form className = "space-y-4" >
620
+ < form className = "space-y-4" onSubmit = { handleSubmit } >
551
621
< div className = "grid gap-2" >
552
622
< label htmlFor = "name" className = "text-sm font-medium" >
553
623
Name
554
624
</ label >
555
625
< input
556
626
id = "name"
627
+ name = "name"
557
628
className = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
558
629
placeholder = "Your name"
559
630
/>
631
+ { errors . name && (
632
+ < p className = "text-sm text-red-500 mt-1" > { errors . name } </ p >
633
+ ) }
560
634
</ div >
561
635
< div className = "grid gap-2" >
562
636
< label htmlFor = "email" className = "text-sm font-medium" >
563
637
Email
564
638
</ label >
565
639
< input
566
640
id = "email"
641
+ name = "email"
567
642
type = "email"
568
643
className = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
569
644
placeholder = "Your email"
570
645
/>
646
+ { errors . email && (
647
+ < p className = "text-sm text-red-500 mt-1" > { errors . email } </ p >
648
+ ) }
571
649
</ div >
572
650
< div className = "grid gap-2" >
573
651
< label htmlFor = "message" className = "text-sm font-medium" >
574
652
Message
575
653
</ label >
576
654
< textarea
577
655
id = "message"
656
+ name = "message"
578
657
className = "flex min-h-[120px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
579
658
placeholder = "Your message"
580
659
/>
660
+ { errors . message && (
661
+ < p className = "text-sm text-red-500 mt-1" > { errors . message } </ p >
662
+ ) }
581
663
</ div >
582
- < Button type = "submit" className = "w-full bg-primary text-white hover:bg-primary/90" >
583
- Send Message
664
+ < Button type = "submit" className = "w-full bg-primary text-white hover:bg-primary/90" disabled = { isSubmitting } >
665
+ { isSubmitting ? 'Sending...' : ' Send Message' }
584
666
</ Button >
585
667
</ form >
586
668
</ CardContent >
0 commit comments