From 38fc38977668bbc518d020244b0c4a70b9c51154 Mon Sep 17 00:00:00 2001 From: Owen Rees Date: Wed, 3 Jun 2026 22:21:11 +0200 Subject: [PATCH] rudimentary contact form boilerplate --- TODO | 3 +- src/pages/Contact.tsx | 3 - src/pages/Contact/Contact.container.tsx | 25 +++++++ src/pages/Contact/Contact.tsx | 90 +++++++++++++++++++++++++ src/routes/contact.tsx | 4 +- 5 files changed, 118 insertions(+), 7 deletions(-) delete mode 100644 src/pages/Contact.tsx create mode 100644 src/pages/Contact/Contact.container.tsx create mode 100644 src/pages/Contact/Contact.tsx diff --git a/TODO b/TODO index a05469c..e18f2fa 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,10 @@ - og / json-sd -- rearrange buttons / header space - collapse on smaller screens - Lichess login - logger - contact form - bring sections headers on all pages other than home page further up (reduce top margin / padding) - set game current position on game load to ply 0 and whatever the FEN is (starting position probably, but could be custom position) - privacy policy - have no access to anything you generate, only keep a count of success/failed pdf generations with timestamp and error message -- fix SSR related console errors etc - a11y and lighthouse fixes - check wcag compliance +- white background? diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx deleted file mode 100644 index 179f73e..0000000 --- a/src/pages/Contact.tsx +++ /dev/null @@ -1,3 +0,0 @@ -const Contact = () =>
Hello "/contact"!
; - -export default Contact; diff --git a/src/pages/Contact/Contact.container.tsx b/src/pages/Contact/Contact.container.tsx new file mode 100644 index 0000000..7c82832 --- /dev/null +++ b/src/pages/Contact/Contact.container.tsx @@ -0,0 +1,25 @@ +import Contact from "#/pages/Contact/Contact.tsx"; + +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"); + + try { + return { success: true, message: "Message sent!" }; + } catch (error) { + // TODO logger + console.error("Error sending message:", error); + return { + success: false, + message: "Failed to send message. Please try again.", + }; + } + }; + + return ; +}; + +export default ContactContainer; diff --git a/src/pages/Contact/Contact.tsx b/src/pages/Contact/Contact.tsx new file mode 100644 index 0000000..5d10898 --- /dev/null +++ b/src/pages/Contact/Contact.tsx @@ -0,0 +1,90 @@ +import { useActionState } from "react"; +import Section from "#/components/Section.tsx"; + +interface IFormField { + fieldName: string; + type: "text" | "email" | "textarea"; + placeholder: string; + label: string; + required: boolean; +} + +type IProps = { + handleSubmit: ( + prevState: unknown, + formData: FormData, + ) => Promise<{ success: boolean; message: string }>; +}; + +const FormField = ({ + type, + fieldName, + placeholder, + label, + required, +}: IFormField) => ( +
+ + +
+); + +const Contact = ({ handleSubmit }: IProps) => { + const [state, formAction, isPending] = useActionState(handleSubmit, null); + + return ( +
+
+
+ + + + + + +