discord server component and toast on state change

This commit is contained in:
2026-06-04 22:30:29 +02:00
parent 03e6c3fe31
commit 021841beec
4 changed files with 76 additions and 6 deletions
+21
View File
@@ -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]);
};
+14 -5
View File
@@ -1,17 +1,26 @@
import Contact from "#/pages/Contact/Contact.tsx"; import Contact from "#/pages/Contact/Contact.tsx";
import { sendToDiscord } from "#/server/contact.ts";
const ContactContainer = () => { const ContactContainer = () => {
const handleSubmit = async (_prevState: unknown, data: FormData) => { const handleSubmit = async (_prevState: unknown, data: FormData) => {
const name = data.get("name"); const payload = {
const email = data.get("email"); name: (data.get("name") as string) || "",
const subject = data.get("subject"); email: (data.get("email") as string) || "",
const message = data.get("message"); subject: (data.get("subject") as string) || "",
message: (data.get("message") as string) || "",
};
try { try {
return { success: true, message: "Message sent!" }; await sendToDiscord({ data: payload });
return {
success: true,
message: "Message sent successfully!",
};
} catch (error) { } catch (error) {
// TODO logger // TODO logger
console.error("Error sending message:", error); console.error("Error sending message:", error);
return { return {
success: false, success: false,
message: "Failed to send message. Please try again.", message: "Failed to send message. Please try again.",
+3 -1
View File
@@ -1,6 +1,7 @@
import { useActionState } from "react"; import { useActionState } from "react";
import { LuSend } from "react-icons/lu"; import { LuSend } from "react-icons/lu";
import Section from "#/components/Section.tsx"; import Section from "#/components/Section.tsx";
import { useToastStateChange } from "#/hooks/useToastStateChange.ts";
interface IFormField { interface IFormField {
fieldName: string; fieldName: string;
@@ -56,6 +57,7 @@ const FormField = ({
const Contact = ({ handleSubmit }: IProps) => { const Contact = ({ handleSubmit }: IProps) => {
const [state, formAction, isPending] = useActionState(handleSubmit, null); const [state, formAction, isPending] = useActionState(handleSubmit, null);
useToastStateChange({ state });
return ( return (
<main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]"> <main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]">
@@ -103,7 +105,7 @@ const Contact = ({ handleSubmit }: IProps) => {
{isPending ? "Sending..." : "Send"} {isPending ? "Sending..." : "Send"}
<LuSend className="w-5 h-5" /> <LuSend className="w-5 h-5" />
</button> </button>
{state && <p>{state.message}</p>} {/*{state && <p>{state.message}</p>}*/}
</form> </form>
</Section> </Section>
</main> </main>
+38
View File
@@ -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 };
});