From 021841beecc15dcdb08c356c4a9f4310bd800281 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 4 Jun 2026 22:30:29 +0200 Subject: [PATCH] discord server component and toast on state change --- src/hooks/useToastStateChange.ts | 21 ++++++++++++++ src/pages/Contact/Contact.container.tsx | 19 +++++++++---- src/pages/Contact/Contact.tsx | 4 ++- src/server/contact.ts | 38 +++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 src/hooks/useToastStateChange.ts create mode 100644 src/server/contact.ts diff --git a/src/hooks/useToastStateChange.ts b/src/hooks/useToastStateChange.ts new file mode 100644 index 0000000..2e3066c --- /dev/null +++ b/src/hooks/useToastStateChange.ts @@ -0,0 +1,21 @@ +import { useEffect } from "react"; +import { toast } from "react-toastify"; + +interface IProps { + state: { + success: boolean; + message: string; + } | null; +} + +export const useToastStateChange = ({ state }: IProps) => { + useEffect(() => { + if (!state) return; + + if (state.success) { + toast.success(state.message); + } else { + toast.error(state.message); + } + }, [state]); +}; diff --git a/src/pages/Contact/Contact.container.tsx b/src/pages/Contact/Contact.container.tsx index 7c82832..8ea2a1c 100644 --- a/src/pages/Contact/Contact.container.tsx +++ b/src/pages/Contact/Contact.container.tsx @@ -1,17 +1,26 @@ import Contact from "#/pages/Contact/Contact.tsx"; +import { sendToDiscord } from "#/server/contact.ts"; const ContactContainer = () => { const handleSubmit = async (_prevState: unknown, data: FormData) => { - const name = data.get("name"); - const email = data.get("email"); - const subject = data.get("subject"); - const message = data.get("message"); + const payload = { + name: (data.get("name") as string) || "", + email: (data.get("email") as string) || "", + subject: (data.get("subject") as string) || "", + message: (data.get("message") as string) || "", + }; try { - return { success: true, message: "Message sent!" }; + await sendToDiscord({ data: payload }); + + return { + success: true, + message: "Message sent successfully!", + }; } catch (error) { // TODO logger console.error("Error sending message:", error); + return { success: false, message: "Failed to send message. Please try again.", diff --git a/src/pages/Contact/Contact.tsx b/src/pages/Contact/Contact.tsx index e26e42e..5a64ad2 100644 --- a/src/pages/Contact/Contact.tsx +++ b/src/pages/Contact/Contact.tsx @@ -1,6 +1,7 @@ import { useActionState } from "react"; import { LuSend } from "react-icons/lu"; import Section from "#/components/Section.tsx"; +import { useToastStateChange } from "#/hooks/useToastStateChange.ts"; interface IFormField { fieldName: string; @@ -56,6 +57,7 @@ const FormField = ({ const Contact = ({ handleSubmit }: IProps) => { const [state, formAction, isPending] = useActionState(handleSubmit, null); + useToastStateChange({ state }); return (
@@ -103,7 +105,7 @@ const Contact = ({ handleSubmit }: IProps) => { {isPending ? "Sending..." : "Send"} - {state &&

{state.message}

} + {/*{state &&

{state.message}

}*/}
diff --git a/src/server/contact.ts b/src/server/contact.ts new file mode 100644 index 0000000..846b182 --- /dev/null +++ b/src/server/contact.ts @@ -0,0 +1,38 @@ +import { createServerFn } from "@tanstack/react-start"; +import { env } from "#/env.ts"; + +export const sendToDiscord = createServerFn({ method: "POST" }) + .inputValidator( + (data: { name: string; email: string; subject: string; message: string }) => + data, + ) + .handler(async ({ data }) => { + const response = await fetch(env.DISCORD_CONTACT_WEBHOOK, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + embeds: [ + { + title: "Contact Form Submission", + fields: [ + { name: "Name", value: data.name.trim() || "Not provided" }, + { name: "Email", value: data.email.trim() || "Not provided" }, + { name: "Subject", value: data.subject.trim() || "No Subject" }, + { + name: "Message", + value: data.message.trim() || "Empty message", + }, + ], + }, + ], + }), + }); + + if (!response.ok) { + throw new Error("Discord API rejected the request"); + } + + return { success: true }; + });