diff --git a/docs/ADMIN.md b/docs/ADMIN.md new file mode 100644 index 0000000..f4f43e1 --- /dev/null +++ b/docs/ADMIN.md @@ -0,0 +1,208 @@ +# Admin Panel Guide + +The admin panel lives at `/admin/` and is restricted to users whose email is in `ADMIN_EMAILS`. +Dev shortcut: `GET /auth/dev-login?email=` (DEBUG mode only). + +Sidebar navigation (left to right in layout): +**Dashboard → Marketplace → Leads → Suppliers → Flags → Feedback → Emails → pSEO → SEO** + +CMS sections (Templates, Scenarios, Articles) are linked from the sidebar too. + +--- + +## Dashboard `/admin/` + +Quick-glance overview: user count, lead funnel summary, recent tasks, and credit economy totals. Entry point for everything else. + +--- + +## Marketplace `/admin/marketplace` + +Single-screen health view for the two-sided market. Useful for daily ops check and spotting stalled leads or low supplier engagement. + +**Lead Funnel** (top cards) +| Card | Meaning | +|------|---------| +| Total Leads | All quote-type leads ever submitted | +| Verified New | Verified leads with status `new` — ready to be unlocked by a supplier | +| Unlocked | Distinct leads that have at least one `lead_forward` record | +| Won | Leads with status `closed_won` | +| Conversion Rate | Won ÷ Total | + +**Credit Economy** +| Card | Meaning | +|------|---------| +| Issued | Sum of all positive credit ledger entries (purchases + refills) | +| Consumed | Credits spent on lead unlocks (absolute value of `lead_unlock` entries) | +| Outstanding | Current `credit_balance` sum across all paid-tier suppliers | +| 30-day Burn | Credits consumed in the last 30 days | + +**Supplier Engagement** +| Card | Meaning | +|------|---------| +| Active | Paid-tier suppliers with `credit_balance > 0` | +| Avg Unlocks | Average lead forwards per active supplier | +| Response Rate | Forwards where `status != 'sent'` ÷ total forwards | + +**Feature Flags** — `lead_unlock` and `supplier_signup` toggles are inline on this page. Clicking saves immediately and refreshes (no full-page reload). Also accessible at `/admin/flags`. + +**Activity Stream** — HTMX partial loaded on page open, showing the last 50 events across three tables: new leads, lead unlocks, credit ledger entries. Three dot colours: blue = lead created, green = unlock, amber = credit event. + +--- + +## Leads `/admin/leads` + +**List view** (`/admin/leads`) + +Summary cards at top: total leads / new+unverified / hot pipeline credits / forward rate. + +Filters (HTMX live — updates table without page reload): +- **Search** — matches `contact_name`, `contact_email`, `contact_company` +- **Status** — new / pending_verification / contacted / forwarded / closed_won / closed_lost +- **Heat** — hot / warm / cool +- **Country** — ISO country code +- **Period** pills — Today / 7d / 30d / All + +**Detail view** (`/admin/leads/`) + +Full lead record including all extended quote fields (build context, glass type, lighting, location status, financing, services needed, notes). + +Inline HTMX actions (no full-page reload): +- **Status change** — dropdown + save button → swaps the status badge in place +- **Forward to supplier** — select supplier + send → appends row to forward history table + +Forward history table shows: supplier name, current forward status, credit cost, sent timestamp. + +**Create lead** (`/admin/leads/new`) — manual lead entry for phone/offline enquiries. + +**Heat scoring** — set automatically on submission: hot = 35 credits (≥6 courts + confirmed budget + short timeline), warm = 20, cool = 8. + +--- + +## Suppliers `/admin/suppliers` + +**List view** — search by name/email, filter by tier (free/basic/growth/pro) and country. Table shows tier badge, credit balance, and listing status. + +**Detail view** (`/admin/suppliers/`) +- Adjust credit balance (add or subtract, reason logged to `credit_ledger`) +- Change subscription tier +- View all lead forwards for this supplier with forward statuses +- Impersonate supplier (enter their session to debug dashboard issues) + +**Create supplier** (`/admin/suppliers/new`) — manual supplier onboarding. + +--- + +## Feature Flags `/admin/flags` + +Toggle on/off without redeploy. Current flags: + +| Flag | Controls | +|------|---------| +| `markets` | Market score pages visible to public | +| `payments` | Paddle checkout enabled | +| `planner_export` | PDF export tab visible in planner | +| `supplier_signup` | Supplier signup wizard accessible | +| `lead_unlock` | Suppliers can unlock leads (spend credits) | + +Flags can also be toggled inline on the Marketplace dashboard for the two most-used flags. + +--- + +## Feedback `/admin/feedback` + +All submissions from the on-page feedback widget (thumbs up/down + optional text). Filterable by page and rating. Rate-limited to 1 per IP per page per hour. + +--- + +## Emails + +### Sent Log `/admin/emails` + +Every outgoing email recorded in `email_sent`. Filter by type (magic_link, lead_forward, weekly_digest, etc.), delivery event (delivered/bounced/opened/clicked), or free-text search on recipient/subject. + +Clicking a row opens the **detail view** — shows metadata, Resend delivery event timeline, and a sandboxed HTML preview of the message body fetched live from Resend API. + +### Inbox `/admin/emails/inbox` + +Inbound emails received via Resend inbound routing. Unread count shown as a badge in the sidebar. Detail view renders the HTML body in a sandboxed iframe with an inline reply form. + +### Compose `/admin/emails/compose` + +Send one-off transactional or plain emails. Select from-address (leads@, hello@, etc.) and optionally wrap in the branded email shell. + +### Audiences `/admin/emails/audiences` + +Lists all Resend audiences (waitlist, planner nurture, etc.) with contact counts. Drill into an audience to view contacts and remove individuals. + +--- + +## pSEO Engine `/admin/pseo` + +Operational visibility for the programmatic SEO content pipeline. Four sub-tabs: + +**Content Gaps** — for each template, shows DuckDB serving rows that have no matching article in the requested language. Use this to prioritise what to generate next. + +**Health Checks** — per-article sanity checks: +- hreflang orphans (EN article exists, DE missing) +- missing HTML build files on disk +- broken `[scenario:slug]` references in article markdown + +**Freshness** — compares `_serving_meta.json` export timestamp vs `MAX(updated_at)` across articles per template. Status: 🟢 Fresh / 🟡 Stale / 🟣 No articles / ⚫ No data. + +**Jobs** — live generation job monitor. Progress bars poll every 2s while jobs run. Error drilldown via `
` on failed jobs. + +Generation is triggered from the **Templates** section (see below) — pSEO Engine is read-only observability. + +--- + +## SEO Hub `/admin/seo` + +Aggregated SEO metrics from Google Search Console, Bing Webmaster Tools, and Umami. Three tabs: + +- **Search** — keyword performance (impressions, clicks, CTR, position) synced daily +- **Funnel** — Umami pageview → planner → quote conversion funnel +- **Scorecard** — per-article GSC impressions/clicks overlay on article metadata + +**Sync** button triggers an immediate background sync of all configured sources (otherwise syncs daily via scheduler). + +Requires `GSC_CLIENT_SECRETS_JSON`, `GSC_PROPERTY_URL`, `BING_API_KEY`, and `UMAMI_*` env vars. + +--- + +## CMS + +### Templates `/admin/templates` + +Each template = a Jinja2 Markdown template file + a DuckDB data source query. Templates produce articles at scale. + +Actions per template: +- **Edit** — modify template body, data query, or metadata +- **Preview** — render a single row through the template without saving +- **Generate** — bulk generate articles for all (or specific) data rows; runs as background task; progress visible in pSEO Engine → Jobs + +### Scenarios `/admin/scenarios` + +Public scenario cards shown on the landing page (e.g. "6-court indoor club in Munich"). Each has a name, description, financial state blob (pre-fills the planner), and a live PDF preview. + +### Articles `/admin/articles` + +Generated article records. Filter by template, language, country, published status. + +Per-article actions: **Edit** markdown inline, **Publish/Unpublish** (HTMX, no page reload), **Rebuild HTML** (re-runs Markdown → HTML without re-generating content), **Delete**. + +**Rebuild All** button at top re-processes every published article's Markdown into HTML — use after template or CSS changes. + +--- + +## Tasks `/admin/tasks` + +Worker queue state. Shows pending / running / failed tasks with payload and error log. Actions: **Retry** (re-enqueues), **Delete** (removes from queue). + +Failed tasks do not auto-retry — manual retry is intentional so you can inspect the error first. + +--- + +## Users `/admin/users` + +List all users (search by email/name). Detail view shows role, subscription state, scenarios, and recent activity. **Impersonate** button logs you in as that user — "Stop impersonating" in the top bar returns you to your admin session.