@@ -10,15 +10,85 @@ import { ThemeToggle } from "@/components/theme-toggle"
1010import { useState } from "react"
1111import { useSmoothScroll } from "@/hooks/use-smooth-scroll"
1212import Script from "next/script"
13+ import { z } from "zod"
14+ import { toast } from "sonner"
1315
1416// Skill component for highlighting technologies
1517const Skill = ( { children } : { children : React . ReactNode } ) => (
1618 < strong className = "text-primary" > { children } </ strong >
1719) ;
1820
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+
1930export default function Home ( ) {
2031 const [ mobileMenuOpen , setMobileMenuOpen ] = useState ( false )
2132 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+ } ;
2292
2393 return (
2494 < div className = "min-h-screen bg-background" >
@@ -188,7 +258,7 @@ export default function Home() {
188258 </ div >
189259 < div className = "flex-1 flex justify-center md:justify-end" >
190260 < 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 />
192262 </ div >
193263 </ div >
194264 </ section >
@@ -547,40 +617,52 @@ export default function Home() {
547617 </ div >
548618 < Card className = "hover:shadow-md hover:shadow-primary/10 transition-shadow" >
549619 < CardContent className = "p-6" >
550- < form className = "space-y-4" >
620+ < form className = "space-y-4" onSubmit = { handleSubmit } >
551621 < div className = "grid gap-2" >
552622 < label htmlFor = "name" className = "text-sm font-medium" >
553623 Name
554624 </ label >
555625 < input
556626 id = "name"
627+ name = "name"
557628 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"
558629 placeholder = "Your name"
559630 />
631+ { errors . name && (
632+ < p className = "text-sm text-red-500 mt-1" > { errors . name } </ p >
633+ ) }
560634 </ div >
561635 < div className = "grid gap-2" >
562636 < label htmlFor = "email" className = "text-sm font-medium" >
563637 Email
564638 </ label >
565639 < input
566640 id = "email"
641+ name = "email"
567642 type = "email"
568643 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"
569644 placeholder = "Your email"
570645 />
646+ { errors . email && (
647+ < p className = "text-sm text-red-500 mt-1" > { errors . email } </ p >
648+ ) }
571649 </ div >
572650 < div className = "grid gap-2" >
573651 < label htmlFor = "message" className = "text-sm font-medium" >
574652 Message
575653 </ label >
576654 < textarea
577655 id = "message"
656+ name = "message"
578657 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"
579658 placeholder = "Your message"
580659 />
660+ { errors . message && (
661+ < p className = "text-sm text-red-500 mt-1" > { errors . message } </ p >
662+ ) }
581663 </ 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' }
584666 </ Button >
585667 </ form >
586668 </ CardContent >
0 commit comments