# 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=...`
