From 9bf209a387f48032b7a6cb922eb4f7faa149afe3 Mon Sep 17 00:00:00 2001 From: epicmeme Date: Tue, 28 Oct 2025 14:38:05 +0100 Subject: [PATCH 1/4] wip: add signup functionality --- src/components/button.astro | 15 ++++++---- src/components/input.astro | 7 +++++ src/pages/api/signup.ts | 15 ++++++++++ src/pages/join.astro | 10 ------- src/pages/join/index.astro | 58 ++++++++++++++++++++++++++++++++++++ src/pages/join/success.astro | 10 +++++++ 6 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 src/components/input.astro create mode 100644 src/pages/api/signup.ts delete mode 100644 src/pages/join.astro create mode 100644 src/pages/join/index.astro create mode 100644 src/pages/join/success.astro diff --git a/src/components/button.astro b/src/components/button.astro index 37c7add..182a4f2 100644 --- a/src/components/button.astro +++ b/src/components/button.astro @@ -1,7 +1,7 @@ --- -interface Props { +interface Props extends astroHTML.JSX.ButtonHTMLAttributes { variant?: ButtonVariant; - link: boolean; + link?: boolean; } type ButtonVariant = keyof typeof buttonVariants; @@ -15,11 +15,16 @@ const buttonVariants = { "border border-primary text-primary hover:opacity-80 transition-opacity", }; -const { variant = "primary", link = false } = Astro.props; +const { + variant = "primary", + link = false, + class: className, + ...rest +} = Astro.props; -const buttonClass = `${baseButton} ${buttonVariants[variant]} ${link ? "cursor-pointer" : ""}`; +const buttonClass = `${baseButton} ${buttonVariants[variant]} ${link ? "cursor-pointer" : ""} ${className || ""}`; --- - diff --git a/src/components/input.astro b/src/components/input.astro new file mode 100644 index 0000000..f6a3156 --- /dev/null +++ b/src/components/input.astro @@ -0,0 +1,7 @@ +--- +interface Props extends astroHTML.JSX.InputHTMLAttributes {} +const { ...rest } = Astro.props; +--- + + + diff --git a/src/pages/api/signup.ts b/src/pages/api/signup.ts new file mode 100644 index 0000000..47d2c9b --- /dev/null +++ b/src/pages/api/signup.ts @@ -0,0 +1,15 @@ +import type { APIRoute } from "astro"; + +export const prerender = false; + +export const POST: APIRoute = async ({ request }) => { + const data = await request.formData(); + const email = data.get("email"); + const name = data.get("name"); + + console.log(email, name); + + return new Response(JSON.stringify({ email, name }), { + headers: { "Content-Type": "application/json" }, + }); +}; diff --git a/src/pages/join.astro b/src/pages/join.astro deleted file mode 100644 index 86006b7..0000000 --- a/src/pages/join.astro +++ /dev/null @@ -1,10 +0,0 @@ ---- -import Layout from "../layouts/layout.astro"; -import "../styles/global.css"; ---- - - -
-

Nothing to see here, for now...

-
-
diff --git a/src/pages/join/index.astro b/src/pages/join/index.astro new file mode 100644 index 0000000..0d19dcf --- /dev/null +++ b/src/pages/join/index.astro @@ -0,0 +1,58 @@ +--- +export const prerender = false; // Not needed in 'server' mode + +class ValidationError extends Error {} + +if (Astro.request.method === "POST") { + try { + const data = await Astro.request.formData(); + const name = data.get("name"); + const email = data.get("email"); + + const schema = z.object({ + name: z.string().min(2).max(100), + email: z.string().email(), + }); + + const result = schema.safeParse({ name, email }); + + if (!result.success) { + throw new ValidationError("Invalid input"); + } + + return Astro.redirect("/join/success"); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } + + if (error instanceof ValidationError) { + return Astro.redirect("/join?error=invalid"); + } + } +} + +import { z } from "astro:content"; +import Button from "../../components/button.astro"; +import Input from "../../components/input.astro"; +import Layout from "../../layouts/layout.astro"; + +const error = Astro.url.searchParams.get("error"); +--- + + +
+ {error ? "ERROR!" : ""} +
+
+ + +
+
+ + +
+ +
+
+
diff --git a/src/pages/join/success.astro b/src/pages/join/success.astro new file mode 100644 index 0000000..155ddef --- /dev/null +++ b/src/pages/join/success.astro @@ -0,0 +1,10 @@ +--- +import Layout from "../../layouts/layout.astro"; +--- + + +
+

Thank you for applying.

+

We will reach out to you shortly.

+
+
From 9b3435688edf3d1952e1a06ae72769a170bc5da2 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 29 Oct 2025 15:53:56 +0100 Subject: [PATCH 2/4] wip(signup): clearer error message + add gdpr --- src/layouts/layout.astro | 2 +- src/pages/join/index.astro | 31 ++++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/layouts/layout.astro b/src/layouts/layout.astro index 35916ec..49f5897 100644 --- a/src/layouts/layout.astro +++ b/src/layouts/layout.astro @@ -19,7 +19,7 @@ import "../styles/global.css";
- {error ? "ERROR!" : ""} -
+ { + error && ( +
+

Error.

+

{error}

+
+ ) + } + + +
- +
- +

+ By submitting this form, you agree to the + IT Chapter's GDPR Policy. + +

+
From eac90f63ad0b50a5b17f751eb94e14040b87b8d1 Mon Sep 17 00:00:00 2001 From: epicmeme Date: Mon, 3 Nov 2025 14:57:10 +0100 Subject: [PATCH 3/4] wip(signup): always show underline for anchor tags --- src/pages/contact.astro | 2 +- src/pages/join/index.astro | 44 +++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/pages/contact.astro b/src/pages/contact.astro index 1db3d4f..bd77f32 100644 --- a/src/pages/contact.astro +++ b/src/pages/contact.astro @@ -19,7 +19,7 @@ import "../styles/global.css";

You can reach us by contacting init@kth.itinit@kth.it.

diff --git a/src/pages/join/index.astro b/src/pages/join/index.astro index 5299752..4d5e2aa 100644 --- a/src/pages/join/index.astro +++ b/src/pages/join/index.astro @@ -4,33 +4,33 @@ export const prerender = false; // Not needed in 'server' mode class ValidationError extends Error {} if (Astro.request.method === "POST") { - try { - const data = await Astro.request.formData(); - const name = data.get("name"); - const email = data.get("email"); + try { + const data = await Astro.request.formData(); + const name = data.get("name"); + const email = data.get("email"); - const schema = z.object({ - name: z.string().min(2).max(100), - email: z.string().email(), - }); + const schema = z.object({ + name: z.string().min(2).max(100), + email: z.string().email(), + }); - const result = schema.safeParse({ name, email }); + const result = schema.safeParse({ name, email }); - if (!result.success) { - console.error(result.error); - throw new ValidationError("Invalid input"); - } + if (!result.success) { + console.error(result.error); + throw new ValidationError("Invalid input"); + } - return Astro.redirect("/join/success"); - } catch (error) { - if (error instanceof Error) { - console.error(error.message); - } + return Astro.redirect("/join/success"); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } - if (error instanceof ValidationError) { - return Astro.redirect("/join?error=Missing+or+invalid+fields"); - } + if (error instanceof ValidationError) { + return Astro.redirect("/join?error=Missing+or+invalid+fields"); } + } } import { z } from "astro:content"; @@ -65,7 +65,7 @@ const error = Astro.url.searchParams.get("error");

By submitting this form, you agree to the IT Chapter's GDPR Policy. From 261ebad1c7f9e1d024f810ed9423255024d19680 Mon Sep 17 00:00:00 2001 From: epicmeme Date: Wed, 5 Nov 2025 16:12:06 +0100 Subject: [PATCH 4/4] persist form state on error --- src/assets/init.svg | 1 + src/pages/join/index.astro | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/assets/init.svg b/src/assets/init.svg index a35f2f0..cbb01a5 100644 --- a/src/assets/init.svg +++ b/src/assets/init.svg @@ -5,4 +5,5 @@ + init diff --git a/src/pages/join/index.astro b/src/pages/join/index.astro index 4d5e2aa..dd79cf1 100644 --- a/src/pages/join/index.astro +++ b/src/pages/join/index.astro @@ -25,6 +25,7 @@ if (Astro.request.method === "POST") { } catch (error) { if (error instanceof Error) { console.error(error.message); + return Astro.redirect("/join?error=Internal+server+error"); } if (error instanceof ValidationError) { @@ -52,15 +53,14 @@ const error = Astro.url.searchParams.get("error"); ) } - -

+
- +

By submitting this form, you agree to the