-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a23b601
commit 72d9213
Showing
5 changed files
with
229 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
"use server"; | ||
|
||
import { z } from "zod"; | ||
import { redirect } from "next/navigation"; | ||
import PocketBase from "pocketbase"; | ||
import defaultsDeep from "lodash/defaultsDeep"; | ||
|
||
const pb = new PocketBase("https://direction-trade.pockethost.io"); | ||
|
||
const FormSchema = z | ||
.object({ | ||
name: z.string().min(3, "Name should at least be 3 characters"), | ||
message: z.string().min(2, "Message should at least be 2 characters"), | ||
email: z.string().email("Invalid email address").or(z.literal("")), | ||
phone: z | ||
.string() | ||
.min(10, "Phone number should at least be 10 characters") | ||
.or(z.literal("")), | ||
}) | ||
.superRefine((data, ctx) => { | ||
if (!data.email && !data.phone) { | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: "At least one of email or phone must be present", | ||
path: ["email"], // Mark the email field with the custom error | ||
}); | ||
ctx.addIssue({ | ||
code: z.ZodIssueCode.custom, | ||
message: "At least one of email or phone must be present", | ||
path: ["phone"], // Mark the phone field with the custom error | ||
}); | ||
} | ||
}); | ||
|
||
export async function submitContactForm( | ||
_prevState: Record<string, unknown>, | ||
formData: FormData | ||
) { | ||
const data = { | ||
name: formData.get("name"), | ||
email: formData.get("email"), | ||
message: formData.get("message"), | ||
phone: formData.get("phone"), | ||
}; | ||
|
||
const validatedFields = FormSchema.safeParse(data); | ||
|
||
if (!validatedFields.success) { | ||
return { | ||
errors: validatedFields.error.flatten().fieldErrors, | ||
data: defaultsDeep(data, { | ||
email: "", | ||
phone: "", | ||
name: "", | ||
message: "", | ||
}), | ||
}; | ||
} | ||
|
||
await pb.collection("contact_me_santosh").create(validatedFields.data); | ||
|
||
redirect("/contact/thank-you"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Metadata } from "next"; | ||
|
||
export const metadata: Metadata = { | ||
title: "Contact Sai Santosh Kottakota", | ||
description: | ||
"Get in touch with Sai Santosh Kottakota, Tech Lead and experienced developer", | ||
}; | ||
|
||
export default function ContactLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) { | ||
return <div className="min-h-screen bg-black">{children}</div>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
"use client"; | ||
|
||
import { useActionState } from "react"; | ||
import Link from "next/link"; | ||
import { ArrowLeft } from "lucide-react"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Textarea } from "@/components/ui/textarea"; | ||
import { submitContactForm } from "./actions"; | ||
|
||
const initialState = { | ||
data: { | ||
name: "", | ||
email: "", | ||
message: "", | ||
phone: "", | ||
}, | ||
errors: { | ||
name: [], | ||
email: [], | ||
message: [], | ||
phone: [], | ||
}, | ||
}; | ||
|
||
export default function ContactPage() { | ||
const [state, formAction] = useActionState(submitContactForm, initialState); | ||
|
||
return ( | ||
<div className="min-h-screen bg-black text-white flex flex-col"> | ||
<main className="flex-grow container mx-auto px-6 py-12"> | ||
<Link href="/" className="inline-block mb-8"> | ||
<Button variant="ghost"> | ||
<ArrowLeft className="mr-2 h-4 w-4" /> Back to Portfolio | ||
</Button> | ||
</Link> | ||
|
||
<h1 className="text-3xl font-bold mb-8">Contact Me</h1> | ||
|
||
<form action={formAction} className="space-y-6 max-w-md"> | ||
<div> | ||
<label htmlFor="name" className="block text-sm font-medium mb-2"> | ||
Name * | ||
</label> | ||
<Input | ||
id="name" | ||
name="name" | ||
required | ||
defaultValue={state.data.name} | ||
className="bg-gray-900 border-gray-700 text-white" | ||
/> | ||
{state.errors?.name && ( | ||
<> | ||
{state.errors.name.map((error, index) => ( | ||
<p className="mt-1 text-sm text-red-500" key={index}> | ||
{error} | ||
</p> | ||
))} | ||
</> | ||
)} | ||
</div> | ||
|
||
<div> | ||
<label htmlFor="phone" className="block text-sm font-medium mb-2"> | ||
Phone | ||
</label> | ||
<Input | ||
id="phone" | ||
name="phone" | ||
defaultValue={state.data.phone} | ||
className="bg-gray-900 border-gray-700 text-white" | ||
/> | ||
{state.errors?.phone && ( | ||
<> | ||
{state.errors.phone.map((error, index) => ( | ||
<p className="mt-1 text-sm text-red-500" key={index}> | ||
{error} | ||
</p> | ||
))} | ||
</> | ||
)} | ||
</div> | ||
|
||
<div> | ||
<label htmlFor="email" className="block text-sm font-medium mb-2"> | ||
</label> | ||
<Input | ||
id="email" | ||
name="email" | ||
type="email" | ||
defaultValue={state.data.email} | ||
className="bg-gray-900 border-gray-700 text-white" | ||
/> | ||
{state.errors?.email && ( | ||
<> | ||
{state.errors.email.map((error, index) => ( | ||
<p className="mt-1 text-sm text-red-500" key={index}> | ||
{error} | ||
</p> | ||
))} | ||
</> | ||
)} | ||
</div> | ||
|
||
<div> | ||
<label htmlFor="message" className="block text-sm font-medium mb-2"> | ||
Message * | ||
</label> | ||
<Textarea | ||
id="message" | ||
name="message" | ||
required | ||
defaultValue={state.data.message} | ||
className="bg-gray-900 border-gray-700 text-white" | ||
rows={5} | ||
/> | ||
{state.errors?.message && ( | ||
<> | ||
{state.errors.message.map((error, index) => ( | ||
<p className="mt-1 text-sm text-red-500" key={index}> | ||
{error} | ||
</p> | ||
))} | ||
</> | ||
)} | ||
</div> | ||
|
||
<Button type="submit" variant="secondary"> | ||
Send Message | ||
</Button> | ||
</form> | ||
</main> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import Link from "next/link"; | ||
import { Button } from "@/components/ui/button"; | ||
|
||
export default function ThankYouPage() { | ||
return ( | ||
<div className="min-h-screen bg-black text-white flex flex-col items-center justify-center"> | ||
<h1 className="text-3xl font-bold mb-4">Thank You!</h1> | ||
<p className="text-xl mb-8">Your message has been sent successfully.</p> | ||
<Button asChild> | ||
<Link href="/">Return to Portfolio</Link> | ||
</Button> | ||
</div> | ||
); | ||
} |