- Flow 11: note CTA token in forward email + matching notification tasks - Flow 12 (new): supplier lead_respond endpoint + one-click CTA token flow - Flow 13 (was 12): add Marketplace admin dashboard row, update Leads row with search/filter/HTMX inline actions, note HTMX partials Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
248 lines
11 KiB
Markdown
248 lines
11 KiB
Markdown
# User Flows
|
||
|
||
All user-facing flows through the padelnomics app. Use this as the reference when writing E2E tests or auditing coverage.
|
||
|
||
---
|
||
|
||
## 1. Visitor → Planner
|
||
|
||
**Entry:** `/<lang>/` → click "Planner" in nav
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /<lang>/planner/` | Wizard loads with default state (indoor, 6 courts, rent). `s` = default state, `d` = calc results. |
|
||
| 2 | Adjust any slider | `POST /<lang>/planner/calculate` (HTMX, `hx-trigger="input changed delay:200ms"`) → returns `#tab-content` partial |
|
||
| 3 | Switch result tab | `POST /<lang>/planner/calculate` with `activeTab=<tab>` → HTMX swaps `#tab-content` |
|
||
| 4 | View charts | Charts embedded as `<script type="application/json" id="chartX-data">` in response. `initCharts()` in `planner.js` renders them. |
|
||
| 5 | Wizard preview | `#wizPreview` updated OOB (`hx-swap-oob="true"`) with CAPEX / monthly CF / IRR |
|
||
|
||
**Auth required:** No (logged-in users get their default scenario pre-loaded)
|
||
**HTMX partials:** `calculate_response.html`, `wizard_preview.html`
|
||
**Key state:** `s` (validated via `validate_state()`), `d` (calc output via `calc()`)
|
||
|
||
---
|
||
|
||
## 2. Visitor → Quote Request (from Planner)
|
||
|
||
**Entry:** Planner → quote sidebar (>1400px wide) or inline CTA (results tabs, <1400px)
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /<lang>/leads/quote` | Planner passes state via query params — step 1 pre-populated |
|
||
| 2–9 | `POST /<lang>/leads/quote/step/<n>` | HTMX: each step swaps `#q-step` content; progress bar OOB-swapped into `#q-progress` |
|
||
| 9 (submit) | `POST /<lang>/leads/quote` | Standard HTML form POST (not HTMX) |
|
||
| After submit | Verification email sent if guest; or lead created directly if logged in |
|
||
| Verify | `GET /<lang>/leads/verify?token=...&lead=...` | Token validated → lead status updated → success page |
|
||
|
||
**Auth required:** No (guests get email verification; logged-in users skip verification)
|
||
**Key validation:** Step 1: facility_type required. Step 2: country required. Step 5: timeline required. Step 7: stakeholder_type required. Step 9: name, email, phone, consent required.
|
||
**Email sent:** `send_quote_verification` (worker task) — verify URL must include `/<lang>/`
|
||
|
||
---
|
||
|
||
## 3. Visitor → Quote Request (Direct)
|
||
|
||
Same as Flow 2 but arrives at `/<lang>/leads/quote` directly (no planner state). Step 1 shows the full editable form (no pre-fill).
|
||
|
||
---
|
||
|
||
## 4. Visitor → Directory
|
||
|
||
**Entry:** `/<lang>/directory/` via nav
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /<lang>/directory/` | Lists all suppliers, filter UI visible |
|
||
| 2 | Search/filter | `GET /<lang>/directory/results?q=...&country=...&category=...` HTMX swaps `#dir-results` |
|
||
| 3 | Click supplier card | `GET /<lang>/directory/<slug>` — supplier detail page |
|
||
| 4 | Send enquiry | `POST /<lang>/directory/<slug>/enquiry` HTMX swaps `#enquiry-result` |
|
||
| 4b | External link | `GET /<lang>/directory/<slug>/website` → 302 redirect (click-tracking) |
|
||
| 4c | Get quote | `GET /<lang>/directory/<slug>/quote` → redirect to quote wizard |
|
||
|
||
**Auth required:** No
|
||
**Category/country labels:** Must be translated per `g.lang` via `get_directory_labels(lang)`
|
||
|
||
---
|
||
|
||
## 5. Visitor → Signup
|
||
|
||
**Entry:** Any CTA "Create Account" / "Sign up" → `/auth/signup`
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /auth/signup` | Signup form (or waitlist form if `WAITLIST_MODE=true`) |
|
||
| 2 | Submit email | `POST /auth/signup` → sends magic link via `send_welcome` + `send_magic_link` tasks |
|
||
| 3 | Click email link | `GET /auth/verify?token=...` → session created, redirect to `next` param or `/dashboard/` |
|
||
| 4 | Dashboard | `GET /dashboard/` |
|
||
|
||
**Auth required:** No
|
||
**Language detection:** Auth routes have no `<lang>` prefix — lang detected from cookie or `Accept-Language` header via `@bp.before_request`
|
||
|
||
---
|
||
|
||
## 6. Returning User → Login
|
||
|
||
**Entry:** `/auth/login`
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /auth/login` | Login form |
|
||
| 2 | Submit email | `POST /auth/login` → enqueues `send_magic_link` |
|
||
| 3 | Confirmation | `GET /auth/magic-link-sent` |
|
||
| 4 | Click email link | `GET /auth/verify?token=...` → session set, redirect |
|
||
| 5 | Dashboard or prior page | Session `user_id` set |
|
||
|
||
**Dev shortcut:** `GET /auth/dev-login?email=test@example.com` (DEBUG mode only) — instant login, used in tests
|
||
|
||
---
|
||
|
||
## 7. User → Save/Load Scenario
|
||
|
||
**Entry:** Planner while logged in
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | Open scenarios panel | `GET /<lang>/planner/scenarios` HTMX partial — lists saved scenarios |
|
||
| 2 | Save current state | `POST /<lang>/planner/scenarios/save` (JSON body: `{name, state}`) → creates/updates scenario |
|
||
| 3 | Load scenario | `GET /<lang>/planner/scenarios/<id>` → returns scenario JSON → JS populates form |
|
||
| 4 | Set default | `POST /<lang>/planner/scenarios/<id>/default` |
|
||
| 5 | Delete | `DELETE /<lang>/planner/scenarios/<id>` |
|
||
|
||
**Auth required:** Yes (`@login_required`)
|
||
|
||
---
|
||
|
||
## 8. User → Export PDF
|
||
|
||
**Entry:** Planner → "Export" tab/button
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /<lang>/planner/export` | Export options page (or waitlist if `WAITLIST_MODE=true`) |
|
||
| 2 | Checkout | `POST /<lang>/planner/export/checkout` → returns Paddle checkout URL (JSON) |
|
||
| 3 | Paddle checkout | External Paddle overlay/redirect |
|
||
| 4 | Post-checkout | `GET /<lang>/planner/export/success` |
|
||
| 5 | Download PDF | `GET /<lang>/planner/export/<id>` (checks subscription/purchase) |
|
||
|
||
**Auth required:** Yes (`@login_required`)
|
||
**Email sent:** `send_welcome` (if new user), PDF ready notification
|
||
|
||
---
|
||
|
||
## 9. Supplier → Signup
|
||
|
||
**Entry:** `/<lang>/suppliers/signup` or from nav "For Suppliers"
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | `GET /<lang>/suppliers/signup` | Plan selection (or waitlist if `WAITLIST_MODE=true`) |
|
||
| 2–4 | `POST /<lang>/suppliers/signup/step/<n>` | HTMX wizard: step 2 = details, step 3 = credits, step 4 = contact |
|
||
| Checkout | `POST /<lang>/suppliers/signup/checkout` | Returns Paddle URL |
|
||
| Success | `GET /<lang>/suppliers/signup/success` | Post-checkout confirmation |
|
||
|
||
**Auth required:** No
|
||
**Plans:** `basic` (free listing), `growth` (leads), `pro` (pro listing + leads)
|
||
|
||
---
|
||
|
||
## 10. Supplier → Dashboard
|
||
|
||
**Entry:** Login → redirect to `/<lang>/suppliers/dashboard` (if supplier role)
|
||
|
||
| Tab | URL | Notes |
|
||
|-----|-----|-------|
|
||
| Overview | `GET /<lang>/suppliers/dashboard/overview` | Stats: views, leads, credits |
|
||
| Lead Feed | `GET /<lang>/suppliers/dashboard/leads` | Lead cards (teased for basic, unlockable for growth/pro) |
|
||
| Listing | `GET /<lang>/suppliers/dashboard/listing` | Edit supplier profile |
|
||
| Boosts | `GET /<lang>/suppliers/dashboard/boosts` | Purchase credit packs |
|
||
|
||
**Auth required:** Yes — `@_supplier_required` (basic+); lead tabs require `@_lead_tier_required` (growth/pro)
|
||
**Dashboard shell:** `GET /<lang>/suppliers/dashboard` — tabs loaded via HTMX
|
||
|
||
---
|
||
|
||
## 11. Supplier → Unlock Lead
|
||
|
||
**Entry:** Lead feed in supplier dashboard
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1 | View teased lead | `GET /<lang>/suppliers/dashboard/leads` — lead shown with blurred contact info |
|
||
| 2 | Unlock | `POST /<lang>/suppliers/leads/<id>/unlock` — deducts 1 credit, reveals full lead |
|
||
| 3 | Receive email | `send_lead_forward_email` task enqueued — full project brief sent to supplier with one-click CTA link |
|
||
| 4 | Entrepreneur notified | `send_lead_matched_notification` task — notifies entrepreneur a supplier was matched |
|
||
|
||
**Auth required:** Yes — `@_lead_tier_required`
|
||
**Credit check:** Server-side check; if 0 credits → redirect to boosts tab
|
||
**Matching notification:** On quote verification, `notify_matching_suppliers` task auto-notifies growth/pro suppliers whose `service_area` matches the lead's country (max 20 per lead); `send_weekly_lead_digest` sends a Monday 08:00 UTC summary of new matching leads to all paid suppliers
|
||
|
||
---
|
||
|
||
## 12. Supplier → Update Lead Response Status
|
||
|
||
**Entry:** Supplier dashboard leads tab, or one-click CTA link in forward email
|
||
|
||
| Step | URL | Notes |
|
||
|------|-----|-------|
|
||
| 1a | Click "Mark as contacted" in email | `GET /suppliers/leads/cta/<cta_token>` — one-click; advances status `sent` → `contacted`; redirects to `/suppliers/dashboard?tab=leads` |
|
||
| 1b | Update via dashboard | `POST /<lang>/suppliers/leads/<token>/respond` — HTMX; sets `status` and optional `supplier_note`; returns 204 |
|
||
|
||
**Auth required:** CTA link is unauthenticated (token is the credential); dashboard endpoint requires `@_lead_tier_required`
|
||
**Valid statuses:** `sent / viewed / contacted / quoted / won / lost / no_response`
|
||
**Idempotency:** CTA only advances `sent → contacted`; subsequent clicks are no-ops
|
||
|
||
---
|
||
|
||
## 13. Admin Flows
|
||
|
||
**Entry:** `/admin/` (requires `@role_required("admin")`)
|
||
|
||
| Area | URL | What you can do |
|
||
|------|-----|-----------------|
|
||
| Dashboard | `GET /admin/` | Stats overview |
|
||
| Marketplace | `GET /admin/marketplace` | Lead funnel, credit economy, supplier engagement, live activity stream, inline feature flag toggles |
|
||
| Users | `GET /admin/users`, `/admin/users/<id>` | List, view, impersonate |
|
||
| Leads | `GET /admin/leads`, `/admin/leads/<id>` | List (search + period filter + summary cards), view detail, HTMX inline status change + forward to supplier |
|
||
| Suppliers | `GET /admin/suppliers`, `/admin/suppliers/<id>` | List, view, adjust credits, change tier, create |
|
||
| Feedback | `GET /admin/feedback` | View all submitted feedback |
|
||
| Email Sent Log | `GET /admin/emails`, `/admin/emails/<id>` | List all outgoing emails (filter by type/event/search), detail with API-enriched HTML preview |
|
||
| Email Inbox | `GET /admin/emails/inbox`, `/admin/emails/inbox/<id>` | Inbound emails (unread badge), detail with sandboxed HTML, inline reply |
|
||
| Email Compose | `GET /admin/emails/compose` | Send ad-hoc emails with from-address selection + optional branded wrapping |
|
||
| Audiences | `GET /admin/emails/audiences`, `/admin/emails/audiences/<id>/contacts` | Resend audiences, contact list, remove contacts |
|
||
| Article Templates | `GET /admin/templates` | CRUD + bulk generate articles from template+data |
|
||
| Published Scenarios | `GET /admin/scenarios` | CRUD public scenario cards (shown on landing) |
|
||
| Articles | `GET /admin/articles` | CRUD, publish/unpublish, rebuild HTML |
|
||
| Task Queue | `GET /admin/tasks` | View worker tasks, retry/delete failed |
|
||
|
||
**HTMX partials:** `lead_status_badge.html` (status change), `lead_forward_history.html` (forward history), `marketplace_activity.html` (activity stream)
|
||
**Dev shortcut:** `/auth/dev-login?email=<admin-email>` where email is in `config.ADMIN_EMAILS`
|
||
|
||
---
|
||
|
||
## Route Prefix Reference
|
||
|
||
| Blueprint | URL Prefix | Lang-prefixed? |
|
||
|-----------|------------|----------------|
|
||
| `public` | `/<lang>` | Yes |
|
||
| `planner` | `/<lang>/planner` | Yes |
|
||
| `directory` | `/<lang>/directory` | Yes |
|
||
| `leads` | `/<lang>/leads` | Yes |
|
||
| `suppliers` | `/<lang>/suppliers` | Yes |
|
||
| `content` | `/<lang>` (catch-all, registered last) | Yes |
|
||
| `auth` | `/auth` | No |
|
||
| `dashboard` | `/dashboard` | No |
|
||
| `billing` | `/billing` | No |
|
||
| `admin` | `/admin` | No |
|
||
| `webhooks` | `/webhooks` | No |
|
||
|
||
**Language detection for non-prefixed blueprints:** Cookie (`lang`) → `Accept-Language` header → fallback `"en"`
|
||
|
||
---
|
||
|
||
## Known Test Shortcuts
|
||
|
||
- **Dev login (no magic link):** `GET /auth/dev-login?email=...` (only when `DEBUG=True`)
|
||
- **Admin login:** `GET /auth/dev-login?email=<email-in-ADMIN_EMAILS>`
|
||
- **Quote verify URL pattern:** `GET /<lang>/leads/verify?token=...&lead=...`
|
||
- **Auth verify URL pattern:** `GET /auth/verify?token=...`
|