77 lines
3.1 KiB
Markdown
77 lines
3.1 KiB
Markdown
# Survey Invite Tracking — Design
|
|
|
|
> **Date:** 2026-03-04
|
|
> **Status:** Approved
|
|
|
|
## Goal
|
|
|
|
Add invite tracking to the FlowPilot survey so Michael can create personalized links, optionally email them, and see who has/hasn't responded. Each invite token is single-use — one submission per token.
|
|
|
|
## Data Model
|
|
|
|
### New table: `survey_invites`
|
|
|
|
| Column | Type | Notes |
|
|
|--------|------|-------|
|
|
| `id` | UUID PK | |
|
|
| `token` | VARCHAR(32) UNIQUE | Random URL-safe token |
|
|
| `recipient_name` | VARCHAR(255) NOT NULL | Who it's for |
|
|
| `recipient_email` | VARCHAR(255) NULL | Only if emailing |
|
|
| `status` | VARCHAR(20) DEFAULT 'pending' | `pending` or `completed` |
|
|
| `email_sent` | BOOLEAN DEFAULT false | Whether Resend email was sent |
|
|
| `created_at` | TIMESTAMPTZ NOT NULL | |
|
|
| `completed_at` | TIMESTAMPTZ NULL | Set on submission |
|
|
|
|
### Modified table: `survey_responses`
|
|
|
|
Add `invite_id` UUID FK nullable → `survey_invites.id`. Responses from tokenless `/survey` have `invite_id = NULL`.
|
|
|
|
## API Endpoints
|
|
|
|
### Public (no auth)
|
|
|
|
- `GET /api/v1/survey/invite/{token}` — Returns invite status (`{ name, status }`). If `completed`, frontend shows "already submitted" screen. Returns 404 for invalid tokens.
|
|
- `POST /api/v1/survey/submit` — Modified: accepts optional `token` field. If token provided, validates it's `pending`, links the response, and marks invite as `completed`. Returns 409 if token already used.
|
|
|
|
### Admin (super_admin auth)
|
|
|
|
- `POST /api/v1/admin/survey-invites` — Create invite. Body: `{ recipient_name, recipient_email?, send_email? }`. Generates token, optionally sends email. Returns the invite with the full survey URL.
|
|
- `GET /api/v1/admin/survey-invites` — List all invites with status.
|
|
|
|
## Frontend
|
|
|
|
### Survey page changes (`/survey`)
|
|
|
|
- On load, reads `?t=<token>` from URL params
|
|
- If token present: calls `GET /survey/invite/{token}`
|
|
- If `completed` → show "already submitted" screen
|
|
- If `pending` → show survey, include token in submission payload
|
|
- If 404 → show survey without token (treat as open link)
|
|
- If no token: show survey as-is (open access)
|
|
|
|
### Admin page (`/admin/survey-invites`)
|
|
|
|
**Top section: Create Invite**
|
|
- Name input (required) + Email input (optional)
|
|
- "Generate Link" button → creates invite, shows URL with copy button
|
|
- "Send Email" button → creates invite with `send_email: true`, shows confirmation toast
|
|
- "Send Email" only enabled when email field is filled
|
|
|
|
**Bottom section: Invite Table**
|
|
- Columns: Name, Email, Status badge (pending amber / completed green), Sent (email icon or dash), Created date, Completed date
|
|
- Sorted by created_at descending
|
|
|
|
## Email Template
|
|
|
|
Uses existing `EmailService` + Resend pattern. Dark-themed email matching Slate & Ice aesthetic:
|
|
- Subject: "FlowPilot Survey — Your expertise matters"
|
|
- Body: Brief intro, CTA button linking to `/survey?t=<token>`, ~5 minutes note
|
|
- From: existing `FROM_EMAIL` config
|
|
|
|
## Constraints
|
|
|
|
- No token expiration
|
|
- No reminder/resend (keep it simple)
|
|
- Tokenless survey still works for open sharing
|
|
- One submission per invite token (enforced backend + frontend)
|