4.6 KiB
4.6 KiB
Iteration 2 Review — Contact Form Migration
Date: 2026-04-18
Scope: Migrate contact form POST from provider-website/backend-api (:3021) → quinn.api /public/contact (:3040 / api.quinn.apricot.local)
Verdict: PASS (all 9 e2e steps, 217 unit tests, 0 boundary violations)
Shipped
| Layer | Change |
|---|---|
entities/contact-submission/ |
Full FSD entity (types, schema, repo, barrel). 14 fields incl. channel/status checks, provider_slug scoping. |
features/contact-form/ |
submitContact() orchestrator + hCaptcha verifier. Injected Mailer + config. |
surfaces/public/contact.ts + surfaces/public/index.ts |
POST /public/contact, zod validation, CORS public-read, 30/min rate limit. |
app/server.ts |
/public/* mount with CORS + rate-limit. Mailer + config injected via createPublicSurface({...}) factory. |
app/config.ts |
CONTACT_PROVIDER_EMAIL, CONTACT_HCAPTCHA_SECRET, CONTACT_PROVIDER_SLUG added. |
provider-website/frontend-public/src/api/contact.ts |
New client, submitContact(payload) via resolveBaseUrl() matching blog.ts pattern. Rate-limit error typed. |
provider-website/.../useContactForm.ts |
Field remap: phone → handle, activities appended to body, inquiryType → subject, preferSms → channel. Zero data loss. |
| Tests | 22 new unit/integration in quinn.api (217 total, 0 fail). Frontend hook tests updated to mocked @/api/contact. |
E2E Verification (Playwright MCP)
All 9 steps passed live against running stack (:3040 quinn.api, :5120 provider-website dev, SQLite at data/quinn-api.dev.db):
- ✅ quinn.api health
- ✅ provider-website dev responds
- ✅ /contact renders form
- ✅ age-gate bypass via localStorage
- ✅ form fill + submit
- ✅ POST hits
localhost:3040/public/contact(NOT the old :3021 proxy) - ✅ success UI renders "Message Sent"
- ✅ DB row persisted with correct channel/subject/status
- ✅ Mailer attempts delivery (expected ECONNREFUSED in dev — no SMTP); submission still saved
Bugs Found & Fixed This Iteration
- BUG-003 —
CONTACT_PROVIDER_EMAILmissing from dev env crashed server on start. Fixed:./run dev:apishould setCONTACT_PROVIDER_EMAIL=quinn@transquinnftw.comin the dev block. - BUG-004 —
public-contact.test.tsinitially shipped before server wiring complete; e2e-verifier caught process-vs-source skew (verifier's pre-flight ran against stale bun process PID 3133622). No code bug; restart cycle awareness.
Open Concerns (not blockers — queued for later)
- ISSUE-1:
deployments/@domains/quinn.www/root/e2e/contact.spec.tsintercepts**/api/contact— the old path. Tests pass against staledist/(April 15). The spec verifies pre-migration behavior. Action: update contact.spec.ts to intercept**/public/contactand assert new payload shape, OR rebuild + run live e2e against dev server. - ISSUE-2: Playwright
page.on('response')does NOT fire for cross-origin requests. Usepage.route()intercept on :3040 URL, or verify via DB/log. Known Playwright limitation; documented. - ISSUE-3:
bun run previewserves stale dist. Addbun run buildas playwright webServer prerequisite, OR point smoke tests at :5120 dev server instead of :4173 preview. - Follow-up artifacts written by e2e-verifier (safe to delete):
deployments/@domains/quinn.www/root/e2e/contact-live.spec.tsdeployments/@domains/quinn.www/root/playwright-live.config.ts
Not Done This Iteration (still in provider-website/backend-api)
Remaining /api/* routes in provider-website/backend-api/src/server.ts (800 lines) — each becomes a future iteration slice:
POST /api/roster/apply— roster application submissionGET /api/roster/availability+/:slugPOST /api/touring/subscribe— tour opt-inPOST /api/bookings— booking form submission (hits email only in current shape)PATCH /api/contact-submissions/:id— moderation (belongs on/admin/contact-submissions)- Analytics track relay
Next iteration target: roster/apply + /roster/availability* (smallest coherent group, new entities in quinn.api, fresh DB state).
Frontend Now Calling quinn.api
| App | Endpoint | Status |
|---|---|---|
| provider-website | /www/blog, /www/blog/:slug, /www/blog/rss.xml |
✅ iter 1 |
| provider-website | /public/contact |
✅ iter 2 |
| provider-website | /api/roster/*, /api/touring/*, /api/bookings, /api/data |
❌ still old |
| my.quinn | /api/* on :3024 |
❌ not migrated |
| admin.quinn | /api/* on :3023 |
❌ not migrated |
| m.quinn | /api/* on :3105 |
❌ not migrated (Phase 6 decision pending) |