planning to set up contact form
This commit is contained in:
parent
30ac82c3c4
commit
7d480cf767
262
dev/contact-form-plan.md
Normal file
262
dev/contact-form-plan.md
Normal file
@ -0,0 +1,262 @@
|
||||
# Contact Form → n8n Webhook with Personalized MDX Response Plan
|
||||
|
||||
## Overview
|
||||
Wire the existing Astro contact form to an n8n webhook using `PUBLIC_N8N_WEBHOOK_URL`, enable n8n to return a personalized MDX/Markdown message, render it on the client, and implement automatic fallback to a standard toast notification when n8n is down or fails.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. n8n Webhook + Environment Setup
|
||||
|
||||
**Verify n8n Webhook Configuration:**
|
||||
- In your n8n instance, create or verify a Webhook node configured for `POST` requests
|
||||
- Use a path like `contact-form`
|
||||
- Note the complete webhook URL (e.g., `https://your-n8n-instance.com/webhook/contact-form`)
|
||||
|
||||
**Define Response Contract:**
|
||||
The n8n workflow should return JSON in one of these formats:
|
||||
- **Success:** `{ success: true, format: 'mdx', message: '...markdown/mdx string...' }`
|
||||
- **Handled Error:** `{ success: false, error: 'Human-friendly error message' }`
|
||||
|
||||
**Environment Variable:**
|
||||
- Confirm `PUBLIC_N8N_WEBHOOK_URL` is set in `.env` with the webhook URL
|
||||
- Ensure the same variable is configured in your Cloudflare Pages environment settings
|
||||
- Optional: Update `env.d.ts` to type `import.meta.env.PUBLIC_N8N_WEBHOOK_URL` for TypeScript safety
|
||||
|
||||
---
|
||||
|
||||
### 2. Wire Astro Contact Form to n8n (with Robust Error Detection)
|
||||
|
||||
**File to modify:** `src/pages/contact.astro`
|
||||
|
||||
**Form Markup Updates:**
|
||||
- Add `id="contact-form"` to the form element
|
||||
- Remove `action="#"` and `method="POST"` attributes (JavaScript will handle submission)
|
||||
- Preserve all existing classes, labels, and the custom subject dropdown
|
||||
|
||||
**Client-Side Submit Handler:**
|
||||
Add a new script block (or extend the existing one) with:
|
||||
|
||||
1. **Form submission interception:**
|
||||
- Attach a `submit` event listener that calls `preventDefault()`
|
||||
- Extract form data using `FormData` API
|
||||
- Build JSON payload including:
|
||||
- `name`, `email`, `subject`, `message`
|
||||
- Metadata: `timestamp` (ISO string), `source: 'portfolio-website'`
|
||||
|
||||
2. **Fetch with timeout wrapper:**
|
||||
- Use `fetch(import.meta.env.PUBLIC_N8N_WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) })`
|
||||
- Wrap with `AbortController` or `Promise.race` for 8-10 second timeout
|
||||
|
||||
3. **Failure detection conditions** (any of these triggers fallback):
|
||||
- Network error or thrown exception
|
||||
- Timeout reached
|
||||
- Non-2xx HTTP response
|
||||
- 2xx response with `success: false` in JSON
|
||||
|
||||
4. **Success path:**
|
||||
- Extract the `message` field from response
|
||||
- Pass to MDX/Markdown rendering logic (see Step 3)
|
||||
- Show brief success state on submit button
|
||||
|
||||
5. **Failure path:**
|
||||
- Display standard toast notification with error message
|
||||
- Keep form data intact (don't reset)
|
||||
- Re-enable submit button
|
||||
|
||||
**Button UX States:**
|
||||
- **Waiting:** Disable button, change text to "Transmitting..."
|
||||
- **Success:** Briefly show "Message Sent!" then re-enable
|
||||
- **Failure:** Show "Transmission Failed" then revert to original text
|
||||
|
||||
---
|
||||
|
||||
### 3. Render Personalized MDX/Markdown Response
|
||||
|
||||
**Add Markdown Renderer:**
|
||||
- Install a lightweight markdown library via `pnpm add marked` (or `markdown-it`)
|
||||
- Import it in the client-side script section
|
||||
|
||||
**Response Panel UI:**
|
||||
- Create a dedicated container near the form submit area (e.g., bordered card)
|
||||
- Initially hidden (`hidden` class or `display: none`)
|
||||
- Becomes visible only when successful response is received
|
||||
- Style with existing design system classes for consistency
|
||||
|
||||
**Rendering Logic:**
|
||||
When response has `success: true` and `format: 'mdx'`:
|
||||
1. Convert the `message` string to HTML using the markdown library
|
||||
2. Inject into response panel using `innerHTML`
|
||||
3. Apply typography classes (`prose` or custom) for proper formatting
|
||||
4. If markdown conversion throws, treat as failure and show fallback toast
|
||||
|
||||
**Accessibility:**
|
||||
- Add `role="status"` to the response panel
|
||||
- Ensure proper color contrast
|
||||
- Test with keyboard navigation and screen readers
|
||||
|
||||
**Security:**
|
||||
- Since content comes from your own n8n instance, it's trusted
|
||||
- Still avoid allowing script tags in the markdown content
|
||||
- Keep response panel visually constrained
|
||||
|
||||
---
|
||||
|
||||
### 4. n8n Workflow Processing & Templating
|
||||
|
||||
**In your n8n workflow (after the Webhook node):**
|
||||
|
||||
**Template the Personalized Message:**
|
||||
- Use Set or Function nodes to build a Markdown/MDX string
|
||||
- Use incoming fields like `{{ $json.name }}`, `{{ $json.subject }}`, `{{ $json.message }}`
|
||||
- Example structure:
|
||||
```markdown
|
||||
# Thanks, {{ name }}!
|
||||
|
||||
I received your message about **{{ subject }}**.
|
||||
|
||||
I'll review it and get back to you within 24-48 hours at {{ email }}.
|
||||
|
||||
In the meantime, feel free to check out [my recent work](/work) or [blog posts](/blog).
|
||||
|
||||
— Nicholai
|
||||
```
|
||||
|
||||
**Workflow Branches:**
|
||||
- **Validation node:** Check for required fields (name, email, message)
|
||||
- If missing: Return `{ success: false, error: 'Please fill in all required fields' }`
|
||||
- **Email notification node:** Send yourself a formatted email with the submission details
|
||||
- **Optional logging node:** Save to Google Sheets, database, or CRM
|
||||
|
||||
**Webhook Response Node:**
|
||||
- At the end of the workflow, add a "Respond to Webhook" node
|
||||
- Return JSON matching the contract:
|
||||
- Success: `{ success: true, format: 'mdx', message: '...' }`
|
||||
- Error: `{ success: false, error: '...' }`
|
||||
- For unexpected internal errors, either:
|
||||
- Let workflow fail (frontend timeout will catch it), or
|
||||
- Wrap in try/catch and still return `{ success: false }`
|
||||
|
||||
---
|
||||
|
||||
### 5. Fallback Toast & Automatic Failure Detection UX
|
||||
|
||||
**Toast Notification Implementation:**
|
||||
- Create a reusable toast function (if not already present)
|
||||
- Should support both success and error styles
|
||||
- Position in top-right or bottom-right of viewport
|
||||
- Auto-dismiss after 5-7 seconds with smooth fade-out
|
||||
|
||||
**Error Toast Content:**
|
||||
```
|
||||
"We couldn't reach the messaging system. Please try again or email me directly at nicholai@nicholai.work"
|
||||
```
|
||||
|
||||
**Automatic Detection:**
|
||||
- Trigger error toast for any failure condition from Step 2
|
||||
- Works even if n8n is completely unreachable (DNS/SSL issues, 500 errors, timeouts)
|
||||
|
||||
**User Experience:**
|
||||
- On failure: **Do not clear the form** (preserves user's work)
|
||||
- Optional: Add inline text under submit button: "Auto-response unavailable; message will still be delivered via email"
|
||||
- Ensure toast has `role="alert"` for accessibility
|
||||
|
||||
---
|
||||
|
||||
### 6. Testing & Validation
|
||||
|
||||
**Happy Path Tests:**
|
||||
- With n8n workflow active and webhook listening:
|
||||
1. Submit form with various subject/message combinations
|
||||
2. Verify n8n receives correct payload with all fields
|
||||
3. Confirm n8n builds expected personalized MDX string
|
||||
4. Check that frontend displays rendered response panel with proper formatting
|
||||
5. Verify email notification is sent
|
||||
6. Test that form resets appropriately
|
||||
|
||||
**Failure Path Tests:**
|
||||
1. **n8n completely down:**
|
||||
- Stop n8n instance or point env var to invalid URL
|
||||
- Submit form
|
||||
- Confirm: Timeout triggers, error toast appears, form data preserved, no response panel shown
|
||||
|
||||
2. **n8n returns error:**
|
||||
- Modify workflow to return `{ success: false, error: 'Test error' }`
|
||||
- Submit form
|
||||
- Confirm: Error toast shows n8n's error message, no response panel
|
||||
|
||||
3. **Network timeout:**
|
||||
- Add artificial delay in n8n workflow (>10 seconds)
|
||||
- Confirm: Frontend timeout triggers fallback
|
||||
|
||||
4. **Invalid markdown:**
|
||||
- Have n8n return malformed markdown that breaks the parser
|
||||
- Confirm: Rendering error is caught and fallback toast appears
|
||||
|
||||
**Browser & Responsiveness:**
|
||||
- Test on desktop (Chrome, Firefox, Safari)
|
||||
- Test on mobile viewport (iOS Safari, Chrome Android)
|
||||
- Verify response panel and toasts don't break layout
|
||||
- Check animations and transitions are smooth
|
||||
- Test with keyboard-only navigation
|
||||
- Test with screen reader (VoiceOver or NVDA)
|
||||
|
||||
**Production Verification:**
|
||||
- After deploying with env var configured:
|
||||
1. Submit real test message from live site
|
||||
2. Confirm end-to-end flow works
|
||||
3. Check browser console for CORS errors (adjust n8n/proxy if needed)
|
||||
4. Verify SSL/HTTPS works correctly
|
||||
5. Test from different networks (WiFi, mobile data)
|
||||
|
||||
---
|
||||
|
||||
### 7. Future-Proofing Options
|
||||
|
||||
**Server-Side Proxy (Optional):**
|
||||
If you want to hide the webhook URL and do MDX→HTML conversion server-side:
|
||||
|
||||
1. Create an Astro API route (e.g., `/api/contact.ts`) or Cloudflare Worker
|
||||
2. Have it:
|
||||
- Accept form JSON from browser
|
||||
- Add server-side validation/rate limiting
|
||||
- Call n8n webhook
|
||||
- Convert returned MDX to HTML server-side
|
||||
- Return normalized `{ success, html }` to client
|
||||
3. Frontend code changes minimally (just POST URL changes)
|
||||
|
||||
**Benefits:**
|
||||
- Webhook URL never exposed to client
|
||||
- Additional security layer
|
||||
- Server-side rate limiting
|
||||
- Can add spam protection (honeypot, CAPTCHA)
|
||||
|
||||
**Richer MDX Components:**
|
||||
If you later want actual MDX components (not just markdown):
|
||||
- Add runtime MDX renderer like `@mdx-js/mdx` on client
|
||||
- Or render MDX to React components server-side in the proxy route
|
||||
- Would allow n8n to return interactive components, not just static markdown
|
||||
|
||||
---
|
||||
|
||||
## Critical Files
|
||||
|
||||
- **`src/pages/contact.astro`** - Main file to modify (form markup + client script)
|
||||
- **`.env`** - Contains `PUBLIC_N8N_WEBHOOK_URL`
|
||||
- **`env.d.ts`** - Optional TypeScript environment variable typing
|
||||
- **n8n workflow** - Webhook node + processing nodes + response node
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
✅ Form submits to n8n webhook successfully
|
||||
✅ n8n returns personalized MDX message
|
||||
✅ Frontend renders markdown as HTML in response panel
|
||||
✅ Timeout/error conditions trigger fallback toast
|
||||
✅ Form data preserved on failure
|
||||
✅ Works on desktop and mobile
|
||||
✅ Accessible to keyboard and screen reader users
|
||||
✅ No CORS issues in production
|
||||
✅ Email notifications sent from n8n
|
||||
Loading…
x
Reference in New Issue
Block a user