feat(contact): implement Formspree contact form submission
Some checks failed
CI / lint-and-test (push) Has been cancelled

- Enable contact form fields with component IDs
- Add callback for Formspree POST with JSON/AJAX
- Include honeypot spam protection (_gotcha field)
- Handle validation, loading, success/error states
- Clear form on successful submission
- Add lessons learned documentation

Closes #92, #93, #94

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-01 14:26:02 -05:00
parent f58b2f70e2
commit a5d6866d63
5 changed files with 307 additions and 15 deletions

View File

@@ -2,6 +2,7 @@
import dash
import dash_mantine_components as dmc
from dash import html
from dash_iconify import DashIconify
dash.register_page(__name__, path="/contact", name="Contact")
@@ -51,51 +52,57 @@ def create_intro_section() -> dmc.Stack:
def create_contact_form() -> dmc.Paper:
"""Create the contact form (disabled in Phase 1)."""
"""Create the contact form with Formspree integration."""
return dmc.Paper(
dmc.Stack(
[
dmc.Title("Send a Message", order=2, size="h4"),
dmc.Alert(
"Contact form submission is coming soon. Please use the direct contact "
"methods below for now.",
title="Form Coming Soon",
color="blue",
variant="light",
),
# Feedback container for success/error messages
html.Div(id="contact-feedback"),
dmc.TextInput(
id="contact-name",
label="Name",
placeholder="Your name",
leftSection=DashIconify(icon="tabler:user", width=18),
disabled=True,
required=True,
),
dmc.TextInput(
id="contact-email",
label="Email",
placeholder="your.email@example.com",
leftSection=DashIconify(icon="tabler:mail", width=18),
disabled=True,
required=True,
),
dmc.Select(
id="contact-subject",
label="Subject",
placeholder="Select a subject",
data=SUBJECT_OPTIONS,
leftSection=DashIconify(icon="tabler:tag", width=18),
disabled=True,
),
dmc.Textarea(
id="contact-message",
label="Message",
placeholder="Your message...",
minRows=4,
disabled=True,
required=True,
),
# Honeypot field for spam protection (hidden from users)
dmc.TextInput(
id="contact-gotcha",
style={"position": "absolute", "left": "-9999px"},
tabIndex=-1,
autoComplete="off",
),
dmc.Button(
"Send Message",
id="contact-submit",
fullWidth=True,
leftSection=DashIconify(icon="tabler:send", width=18),
disabled=True,
),
],
gap="md",
style={"position": "relative"},
),
p="xl",
radius="md",