From 30a3651c17bfd8a771feaaaa4cb700788b757f80 Mon Sep 17 00:00:00 2001 From: Nikhil M Warrier Date: Fri, 13 Mar 2026 08:52:29 +0530 Subject: feat: Add forms --- src/consts.ts | 2 ++ src/content.config.ts | 38 +++++++++++++++++++++++- src/content/forms/test.mdx | 26 +++++++++++++++++ src/lib/data-utils.ts | 5 ++++ src/pages/forms/[...id].astro | 68 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/content/forms/test.mdx create mode 100644 src/pages/forms/[...id].astro (limited to 'src') diff --git a/src/consts.ts b/src/consts.ts index f5e8992..93100c0 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -13,6 +13,8 @@ export const SITE: Site = { eventsPerPage: 3, } +export const FORM_POST_URL = 'https://fosscet-forms.val.run/' + export const NAV_LINKS: SocialLink[] = [ { href: '/blog', diff --git a/src/content.config.ts b/src/content.config.ts index 1bd3526..c9c4810 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -48,6 +48,42 @@ const members = defineCollection({ }), }) +const forms = defineCollection({ + loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/forms' }), + schema: () => + z.object({ + title: z.string(), + description: z.string(), + date: z.coerce.date(), + // sqlite-safe + id: z.string().regex(/^[a-zA-Z0-9_]+$/, { + message: + "Form 'id' can only contain letters, numbers, and underscores.", + }), + submit_text: z.string().default('Submit'), + form: z.object({ + submit_text: z.string().default('Submit'), + fields: z + .array( + z.object({ + id: z.string().regex(/^[a-zA-Z0-9_]+$/, { + message: + "Field 'name' can only contain letters, numbers, and underscores.", + }), + label: z.string(), + type: z + .enum(['text', 'email', 'number', 'textarea', 'tel', 'url']) + .default('text'), + required: z.boolean().default(false), + }), + ) + .min(1, { + message: 'A form must have at least one field defined.', + }), + }), + }), +}) + const projects = defineCollection({ loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/projects' }), schema: ({ image }) => @@ -62,4 +98,4 @@ const projects = defineCollection({ }), }) -export const collections = { events, blog, members, projects } +export const collections = { events, blog, members, projects, forms } diff --git a/src/content/forms/test.mdx b/src/content/forms/test.mdx new file mode 100644 index 0000000..6e3d731 --- /dev/null +++ b/src/content/forms/test.mdx @@ -0,0 +1,26 @@ +--- +title: "Get in Touch" +id: "main_contact_form" +description: "Reach out to the FOSSCET team." +date: 2026-03-13 +form: + submit_text: "Send Message" + fields: + - id: "name" + label: "Your Name" + type: "text" + required: true + - id: "email" + label: "Email Address" + type: "email" + required: true + - id: "message" + label: "What's on your mind?" + type: "textarea" + required: true +--- + +# Let's talk open source. + +This is a sample form. +Everything below this text is generated automatically from the metadata. diff --git a/src/lib/data-utils.ts b/src/lib/data-utils.ts index 6e4439f..114fb97 100644 --- a/src/lib/data-utils.ts +++ b/src/lib/data-utils.ts @@ -26,6 +26,11 @@ export async function getAllEvents(): Promise[]> { return events.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()) } +export async function getAllForms(): Promise[]> { + const forms = await getCollection('forms') + return forms.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()) +} + export function groupEventsByYear( events: CollectionEntry<'events'>[], ): Record[]> { diff --git a/src/pages/forms/[...id].astro b/src/pages/forms/[...id].astro new file mode 100644 index 0000000..713e599 --- /dev/null +++ b/src/pages/forms/[...id].astro @@ -0,0 +1,68 @@ +--- +import { FORM_POST_URL } from '@/consts' +import Layout from '@/layouts/Layout.astro' +import { getAllForms } from '@/lib/data-utils' + +export async function getStaticPaths() { + const forms = await getAllForms() + return forms.map((form) => ({ + params: { id: form.id }, + props: form, + })) +} + +const form = Astro.props.data +const fields = Astro.props.data.form.fields +--- + + +
+
+

+ {form.title} +

+ + { + fields.map((field) => ( +
+ + + {field.type === 'textarea' ? ( +