Files
padelnomics/docs/USER_FLOWS.md
Deeman 4ae00b35d1 refactor: flatten padelnomics/padelnomics/ → repo root
git mv all tracked files from the nested padelnomics/ workspace
directory to the git repo root. Merged .gitignore files.
No code changes — pure path rename.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 00:44:40 +01:00

225 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |
| 29 | `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`) |
| 24 | `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 |
| 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
---
## 12. Admin Flows
**Entry:** `/admin/` (requires `@role_required("admin")`)
| Area | URL | What you can do |
|------|-----|-----------------|
| Dashboard | `GET /admin/` | Stats overview |
| Users | `GET /admin/users`, `/admin/users/<id>` | List, view, impersonate |
| Leads | `GET /admin/leads`, `/admin/leads/<id>` | List, filter, view detail, change status, forward to supplier, create |
| Suppliers | `GET /admin/suppliers`, `/admin/suppliers/<id>` | List, view, adjust credits, change tier, create |
| Feedback | `GET /admin/feedback` | View all submitted feedback |
| 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 |
**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 |
**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=...`