diff --git a/env.sample b/env.sample index 4669820..40b2873 100644 --- a/env.sample +++ b/env.sample @@ -1,2 +1,4 @@ HASURA_GRAPHQL_ADMIN_SECRET= -NEXT_PUBLIC_API_URL= \ No newline at end of file +NEXT_PUBLIC_API_URL= +EMAIL_REFERRALS_API_URL= +FORM_INGEST_API_KEY= diff --git a/src/app/api/email-referrals/route.ts b/src/app/api/email-referrals/route.ts new file mode 100644 index 0000000..76e239d --- /dev/null +++ b/src/app/api/email-referrals/route.ts @@ -0,0 +1,91 @@ +import { NextRequest, NextResponse } from "next/server"; +import { z } from "zod"; + +const emailReferralSchema = z.object({ + email: z.email("Valid email is required"), + referral: z.string().optional(), +}); + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const validationResult = emailReferralSchema.safeParse(body); + + if (!validationResult.success) { + const errors = validationResult.error.issues.map((issue) => ({ + field: issue.path.join("."), + message: issue.message, + })); + + return NextResponse.json( + { + success: false, + error: "Validation failed", + details: errors, + }, + { status: 400 } + ); + } + + const apiUrl = process.env.EMAIL_REFERRALS_API_URL; + const apiKey = process.env.FORM_INGEST_API_KEY; + + if (!apiUrl || !apiKey) { + return NextResponse.json( + { + success: false, + error: "Email referral API is not configured", + }, + { status: 500 } + ); + } + + const response = await fetch(apiUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-form-api-key": apiKey, + }, + body: JSON.stringify(validationResult.data), + }); + + const contentType = response.headers.get("content-type") || ""; + const responseBody = contentType.includes("application/json") + ? await response.json() + : await response.text(); + + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: + (typeof responseBody === "object" && + responseBody && + "error" in responseBody && + responseBody.error) || + responseBody || + "Failed to submit email referral", + }, + { status: response.status } + ); + } + + return NextResponse.json( + { + success: true, + data: responseBody || { ok: true }, + }, + { status: 200 } + ); + } catch (error) { + console.error("Error submitting email referral:", error); + + return NextResponse.json( + { + success: false, + error: "Failed to submit email referral", + }, + { status: 500 } + ); + } +} diff --git a/src/app/join/page.tsx b/src/app/join/page.tsx index a5b481c..8f2f142 100644 --- a/src/app/join/page.tsx +++ b/src/app/join/page.tsx @@ -9,7 +9,16 @@ import CohortValueSection from "@/components/CohortValueSection"; import CohortJoinBanner from "@/components/CohortJoinBanner"; import JoinUsSection from "@/components/JoinUsSection"; -export default function Home() { +type JoinPageProps = { + searchParams?: { + ref?: string; + referral?: string; + }; +}; + +export default function Home({ searchParams }: JoinPageProps) { + const referral = searchParams?.referral ?? searchParams?.ref; + return (
@@ -26,7 +35,7 @@ export default function Home() { - +
diff --git a/src/components/JoinUs.tsx b/src/components/JoinUs.tsx index 2854afd..26d64d1 100644 --- a/src/components/JoinUs.tsx +++ b/src/components/JoinUs.tsx @@ -5,7 +5,6 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useState } from "react"; import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; import { Form, FormControl, @@ -14,11 +13,7 @@ import { FormLabel, RequiredFieldIndicator, } from "@/components/ui/form"; -import { - joinUsFormSchema, - type JoinUsFormData, - transformApplicationDataToApiFormat, -} from "@/lib/validation"; +import { joinUsFormSchema, type JoinUsFormData } from "@/lib/validation"; import Image from "next/image"; import { trackEvent } from "fathom-client"; @@ -29,7 +24,11 @@ const joinUsImages = [ "/images/join-image-2-c.webp", ]; -export default function JoinUs() { +type JoinUsProps = { + referral?: string; +}; + +export default function JoinUs({ referral }: JoinUsProps) { // Deterministic image selection based on 8-minute intervals (no flash, no hydration mismatch) const interval = Math.floor(Date.now() / (1000 * 60 * 8)); // 8 minutes const imageSrc = joinUsImages[interval % joinUsImages.length]; @@ -39,23 +38,17 @@ export default function JoinUs() { "idle" | "success" | "error" >("idle"); const [errorMessage, setErrorMessage] = useState(""); - const form = useForm({ resolver: zodResolver(joinUsFormSchema), defaultValues: { - name: "", email: "", - discordHandle: "", - showcaseComments: "", - showcaseUrl: "", - introduction: "", }, }); const onSubmit = async (data: JoinUsFormData) => { if (isSubmitting) return; // Prevent multiple submissions - console.log("Join Us form submitted:", data); + console.log("Join Us email submitted:", data); // Reset states setIsSubmitting(true); @@ -63,20 +56,21 @@ export default function JoinUs() { setErrorMessage(""); try { - const applicationData = transformApplicationDataToApiFormat(data); - - const response = await fetch("/api/applications", { + const response = await fetch("/api/email-referrals", { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ applicationData }), + body: JSON.stringify({ + email: data.email, + ...(referral ? { referral } : {}), + }), }); const result = await response.json(); if (response.ok) { - console.log("Application submitted successfully:", result); + console.log("Email referral submitted successfully:", result); setSubmissionStatus("success"); //tracking @@ -84,10 +78,10 @@ export default function JoinUs() { // Reset form after successful submission form.reset(); } else { - console.error("Failed to submit application:", result); + console.error("Failed to submit email referral:", result); setSubmissionStatus("error"); setErrorMessage( - result.error || "Failed to submit application. Please try again.", + result.error || "Failed to submit. Please try again.", ); } } catch (error) { @@ -105,7 +99,7 @@ export default function JoinUs() { const SuccessState = () => (

- Your Words Have Been Passed On. + You're On The List.

-
- - Look for the Tavern Keeper in Discord - -
+

+ Watch your inbox for next steps. +

); @@ -184,13 +172,11 @@ export default function JoinUs() { {submissionStatus === "success" ? (

- Thank you for your interest in joining RaidGuild! + Thanks for joining the cohort updates.

) : (

- Ready to embark on your journey and join the ranks? Share - your tale with us—what epic skills await the Guild's - discovery? + Drop your email to start the onboarding journey.

)} @@ -211,103 +197,16 @@ export default function JoinUs() { > ( - - - Name - - - - - - )} - /> -
- ( - - - Email Address - - - - - - )} - /> - ( - - Discord Username - - - - - )} - /> -
- - ( - - - Introduce Yourself - - -