docs: fix phase READMEs and STATE.md — add full scope/plan to phases 9-15, fix stale last-commit field
This commit is contained in:
@@ -44,7 +44,7 @@ Resume file: .paul/ROADMAP.md → Phase 9
|
|||||||
|
|
||||||
Remote: TBD (new Gitea repo)
|
Remote: TBD (new Gitea repo)
|
||||||
Branch: master
|
Branch: master
|
||||||
Last commit: initial scaffold
|
Last commit: feat(phase-8): billing engine + invoice PDF + mark-paid + overdue escalation
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# Phase 09: Email Dispatcher
|
||||||
|
|
||||||
|
**Status:** Not started
|
||||||
|
**Depends on:** Phase 8 (Billing Engine)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Ensure all automated emails (invoices, low credits, license expiry, monthly reports, welcome)
|
||||||
|
send correctly via SMTP with proper HTML templates. Add an email delivery log visible to
|
||||||
|
super admin, and a test-email endpoint to verify SMTP config.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
**Jinja2 HTML email templates** (`backend/app/templates/email/`)
|
||||||
|
- `invoice.html` — branded invoice notification (already plain text in Phase 8; upgrade to HTML)
|
||||||
|
- `low_credit_alert.html` — low credit warning with credit meter visual
|
||||||
|
- `license_expiry.html` — license expiry countdown with days remaining
|
||||||
|
- `monthly_report.html` — monthly report summary email (stub, filled in Phase 11)
|
||||||
|
- `welcome.html` — welcome email with license key + setup instructions (used in Phase 14)
|
||||||
|
- Base layout template with consistent header/footer
|
||||||
|
|
||||||
|
**`email_logs` table** (new model `app/models/email_log.py`)
|
||||||
|
- `id`, `school_id` (nullable), `to_email`, `subject`, `email_type`, `status` (sent/failed),
|
||||||
|
`sent_at`, `error_message`, `created_at`
|
||||||
|
|
||||||
|
**`app/services/email.py`** — enhanced
|
||||||
|
- Accept HTML template name + context instead of raw body string
|
||||||
|
- Log every send attempt to `email_logs` table
|
||||||
|
- Retry on transient failure (up to 3 attempts)
|
||||||
|
|
||||||
|
**`backend/app/routers/email.py`** (new)
|
||||||
|
- `GET /api/email-logs` — super admin: paginated email delivery history with filters
|
||||||
|
- `POST /api/email/test` — send a test email to verify SMTP config (super admin)
|
||||||
|
|
||||||
|
**Update all tasks** that send email (billing, sms, license) to use HTML templates
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
**EmailLogsPage or section** in super admin
|
||||||
|
- Table: to, subject, type, status badge, sent_at, error message
|
||||||
|
- Filter by email type + status
|
||||||
|
|
||||||
|
**api.ts additions**
|
||||||
|
- `getEmailLogs(params)` — list email logs
|
||||||
|
- `sendTestEmail(to)` — trigger test email
|
||||||
|
|
||||||
|
## Key Files to Create/Modify
|
||||||
|
|
||||||
|
- `backend/app/models/email_log.py` (new)
|
||||||
|
- `backend/app/templates/email/*.html` (new — 5 templates)
|
||||||
|
- `backend/app/services/email.py` (enhance)
|
||||||
|
- `backend/app/routers/email.py` (new)
|
||||||
|
- `backend/app/main.py` — include email router
|
||||||
|
- `backend/migrations/versions/002_email_logs.py` (new migration)
|
||||||
|
- `frontend/src/pages/EmailLogsPage.vue` (new or section in existing page)
|
||||||
|
- `frontend/src/lib/api.ts` — add email log functions
|
||||||
|
- `frontend/src/router/index.ts` — add email logs route
|
||||||
|
- `frontend/src/components/sidebar/AppSidebar.vue` — add nav item
|
||||||
|
|||||||
@@ -1,9 +1,64 @@
|
|||||||
# Phase 10: Support Ticket System
|
# Phase 10: Support Ticket System
|
||||||
|
|
||||||
**Status:** Not started
|
**Status:** Not started
|
||||||
|
**Depends on:** Phase 9 (Email Dispatcher — for email notifications)
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Email notifications on tickets, SLA tracking, internal notes, priority escalation.
|
|
||||||
|
|
||||||
## Plans
|
Polish and harden the existing ticket system with SLA tracking display, email notifications
|
||||||
- [ ] TBD — run /paul:plan when Phase 9 is complete
|
on ticket create/reply, priority auto-escalation, and ticket assignment.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
**Email notifications** (using Phase 9 HTML templates)
|
||||||
|
- New ticket created → email to super admin(s)
|
||||||
|
- Super admin replies → email to school admin
|
||||||
|
- School admin replies → email to super admin(s)
|
||||||
|
- Template: `ticket_notification.html`
|
||||||
|
|
||||||
|
**SLA display**
|
||||||
|
- `first_response_at` already stored — compute SLA status in list endpoint:
|
||||||
|
- `sla_status`: `on_track` | `at_risk` (>24h no response) | `breached` (>48h no response)
|
||||||
|
- Add `sla_status` to ticket list response
|
||||||
|
|
||||||
|
**Priority auto-escalation** (new Celery task `tickets.escalate_stale`)
|
||||||
|
- Scheduled every hour
|
||||||
|
- Tickets open > 48h with `priority = normal` → set `priority = high`
|
||||||
|
- Tickets open > 72h with no reply → set `priority = urgent`
|
||||||
|
|
||||||
|
**Ticket assignment**
|
||||||
|
- `assigned_to: str | None` column already on model
|
||||||
|
- `PUT /api/tickets/{id}` already accepts `assigned_to`
|
||||||
|
- Add `GET /api/users?role=super_admin` filter for assignee picker
|
||||||
|
|
||||||
|
**Bulk actions**
|
||||||
|
- `POST /api/tickets/bulk-close` — close list of resolved ticket IDs (super admin)
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
**TicketsPage.vue** — super admin view enhancements
|
||||||
|
- SLA status column (green/amber/red badge)
|
||||||
|
- Assignee column + inline assign dropdown
|
||||||
|
- Priority badge
|
||||||
|
- Bulk close: checkbox selection + "Close Selected" button
|
||||||
|
- Status filter tabs (All / Open / In Progress / Resolved / Closed)
|
||||||
|
|
||||||
|
**TicketDetailPage.vue** — enhancements
|
||||||
|
- Assignee selector
|
||||||
|
- Priority selector
|
||||||
|
- SLA timer display (hours since opened, color-coded)
|
||||||
|
- Internal notes clearly marked with lock icon (super admin only)
|
||||||
|
|
||||||
|
**worker.py** — add `tickets.escalate_stale` to beat schedule (every hour)
|
||||||
|
|
||||||
|
## Key Files to Modify
|
||||||
|
|
||||||
|
- `backend/app/routers/tickets.py` — SLA status in response, bulk-close endpoint
|
||||||
|
- `backend/app/tasks/tickets.py` (new) — escalate_stale task
|
||||||
|
- `backend/app/worker.py` — add escalation task to beat schedule
|
||||||
|
- `backend/app/templates/email/ticket_notification.html` (new)
|
||||||
|
- `frontend/src/pages/TicketsPage.vue` — SLA, assign, bulk close
|
||||||
|
- `frontend/src/pages/TicketDetailPage.vue` — SLA timer, assign, priority
|
||||||
|
- `frontend/src/lib/api.ts` — bulkCloseTickets
|
||||||
|
|||||||
@@ -1,9 +1,64 @@
|
|||||||
# Phase 11: Monthly Report Generation
|
# Phase 11: Monthly Report Generation + Email
|
||||||
|
|
||||||
**Status:** Not started
|
**Status:** Not started
|
||||||
|
**Depends on:** Phase 9 (Email), Phase 12 (On-Prem Data Pull)
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Auto monthly report email to schools with attendance summary and SMS usage.
|
|
||||||
|
|
||||||
## Plans
|
On the 1st of each month, automatically generate and email a comprehensive report to each
|
||||||
- [ ] TBD — run /paul:plan when Phase 10 is complete
|
active school summarising their SMS usage, credit consumption, and invoice for the period.
|
||||||
|
Super admin can manually trigger reports. School portal shows report history.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
**Enhance `reports.send_monthly_reports` Celery task** (currently a stub)
|
||||||
|
- For each active school:
|
||||||
|
1. Fetch SMS stats for prior month (sent, failed, delivery rate, credit burn)
|
||||||
|
2. Fetch attendance data from `school_monthly_stats` (populated by Phase 12)
|
||||||
|
3. Fetch invoice for the period (if exists)
|
||||||
|
4. Render `monthly_report.html` Jinja2 template
|
||||||
|
5. Send email to `school.billing_email`
|
||||||
|
6. Store report record in `monthly_reports` table
|
||||||
|
|
||||||
|
**`monthly_reports` table** (new model)
|
||||||
|
- `id`, `school_id`, `report_month` (YYYY-MM), `email_sent_at`, `report_data` (JSON),
|
||||||
|
`created_at`
|
||||||
|
|
||||||
|
**Manual trigger endpoint**
|
||||||
|
- `POST /api/reports/send/{school_id}` — super admin triggers report for a specific school
|
||||||
|
|
||||||
|
**School portal: report history**
|
||||||
|
- `GET /api/portal/reports` — school admin lists their past reports
|
||||||
|
- `GET /api/portal/reports/{id}` — view report detail (JSON data rendered as HTML)
|
||||||
|
|
||||||
|
**`monthly_report.html`** Jinja2 email template
|
||||||
|
- School name + period header
|
||||||
|
- SMS stats: sent, failed, delivery rate, credit burn
|
||||||
|
- Attendance summary (if available from Phase 12, else "data unavailable")
|
||||||
|
- Invoice summary for the period
|
||||||
|
- Credit balance as of report date
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
**PortalReportsPage.vue** (new portal page)
|
||||||
|
- List of past monthly reports (month, email sent date, SMS count, status)
|
||||||
|
- Click to view report detail
|
||||||
|
|
||||||
|
**Super admin** — manual report trigger button in SchoolDetailPage
|
||||||
|
|
||||||
|
**Router + sidebar** — add /portal/reports route and nav item
|
||||||
|
|
||||||
|
## Key Files to Create/Modify
|
||||||
|
|
||||||
|
- `backend/app/models/report.py` (new — monthly_reports table)
|
||||||
|
- `backend/app/tasks/reports.py` — enhance from stub
|
||||||
|
- `backend/app/routers/reports.py` (new)
|
||||||
|
- `backend/app/templates/email/monthly_report.html` (new)
|
||||||
|
- `backend/app/main.py` — include reports router
|
||||||
|
- `backend/migrations/versions/003_monthly_reports.py` (new)
|
||||||
|
- `frontend/src/pages/portal/PortalReportsPage.vue` (new)
|
||||||
|
- `frontend/src/router/index.ts` — add /portal/reports
|
||||||
|
- `frontend/src/components/sidebar/PortalSidebar.vue` — add Reports nav item
|
||||||
|
- `frontend/src/lib/api.ts` — portal report functions
|
||||||
|
|||||||
@@ -1,9 +1,66 @@
|
|||||||
# Phase 12: On-Prem Monthly Report Pull
|
# Phase 12: On-Prem Monthly Report Pull
|
||||||
|
|
||||||
**Status:** Not started
|
**Status:** Not started
|
||||||
|
**Depends on:** Phase 11 (Monthly Reports)
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Hub pulls attendance data from each TapTrack instance for monthly reports.
|
|
||||||
|
|
||||||
## Plans
|
Hub pulls attendance summary data from each on-prem TapTrack instance on the 1st of the month
|
||||||
- [ ] TBD — run /paul:plan when Phase 11 is complete
|
to populate monthly report data. Fallback: if on-prem is unreachable, report shows
|
||||||
|
"attendance data unavailable" for that section.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### On-Prem Side (TapTrack — separate repo, documented here for reference)
|
||||||
|
|
||||||
|
**New endpoint on TapTrack on-prem:**
|
||||||
|
`GET /api/hub/monthly-report?key={license_key}&month={YYYY-MM}`
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"month": "2026-02",
|
||||||
|
"total_students": 450,
|
||||||
|
"school_days": 20,
|
||||||
|
"present_days_total": 8100,
|
||||||
|
"absent_days_total": 900,
|
||||||
|
"late_days_total": 200,
|
||||||
|
"avg_attendance_rate": 90.0,
|
||||||
|
"sms_sent": 342
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hub Side
|
||||||
|
|
||||||
|
**`school_monthly_stats` table** (new model)
|
||||||
|
- `id`, `school_id`, `report_month` (YYYY-MM date), `total_students`, `school_days`,
|
||||||
|
`present_days_total`, `absent_days_total`, `late_days_total`, `avg_attendance_rate`,
|
||||||
|
`sms_sent`, `pulled_at`, `pull_status` (success/failed/unavailable)
|
||||||
|
|
||||||
|
**New Celery task: `reports.pull_monthly_stats`**
|
||||||
|
- Runs on the 1st at 5:00am (before `send_monthly_reports` at 7:00am)
|
||||||
|
- For each active school with a `last_seen_ip` and `hub_base_url`:
|
||||||
|
- GET `{school.hub_base_url}/api/hub/monthly-report?key={license_key}&month={YYYY-MM}`
|
||||||
|
- On success: upsert `school_monthly_stats` with pull_status=success
|
||||||
|
- On failure/timeout: create record with pull_status=unavailable
|
||||||
|
- Timeout: 10 seconds per school
|
||||||
|
|
||||||
|
**School model** — add `hub_base_url: str | None` column (the on-prem instance URL)
|
||||||
|
- Set automatically from `last_seen_ip` or configured manually by super admin
|
||||||
|
|
||||||
|
**Super admin UI** — `hub_base_url` field in SchoolDetailPage edit panel
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
No new pages needed — data surfaces through monthly reports (Phase 11).
|
||||||
|
|
||||||
|
**SchoolDetailPage.vue** — show `hub_base_url` field in school info + edit modal
|
||||||
|
|
||||||
|
## Key Files to Create/Modify
|
||||||
|
|
||||||
|
- `backend/app/models/school.py` — add `hub_base_url` column
|
||||||
|
- `backend/app/models/report.py` — add `SchoolMonthlyStats` model
|
||||||
|
- `backend/app/tasks/reports.py` — add `pull_monthly_stats` task
|
||||||
|
- `backend/app/worker.py` — schedule pull task at 5am on 1st
|
||||||
|
- `backend/migrations/versions/004_school_monthly_stats.py` (new)
|
||||||
|
- `frontend/src/pages/SchoolDetailPage.vue` — hub_base_url field
|
||||||
|
|||||||
@@ -1,9 +1,51 @@
|
|||||||
# Phase 13: Feature Flags + Suspension
|
# Phase 13: Feature Flags + Service Suspension Logic
|
||||||
|
|
||||||
**Status:** Not started
|
**Status:** Not started
|
||||||
|
**Depends on:** Phase 3 (License Validation), Phase 8 (Billing / Overdue Suspension)
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Per-school feature flag overrides; suspension propagation to on-prem.
|
|
||||||
|
|
||||||
## Plans
|
Hub controls which features each school's on-prem can use via tier-based + per-school
|
||||||
- [ ] TBD — run /paul:plan when Phase 12 is complete
|
feature flag overrides. Suspension from billing (Phase 8) propagates to on-prem via
|
||||||
|
the sync poll. Super admin can enable beta features per school.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
**`feature_overrides` column on School** (new)
|
||||||
|
- `feature_overrides: dict | None` — JSON column for per-school flag overrides
|
||||||
|
- Example: `{"webhooks": true, "beta_reports": true}` to enable premium features on a basic tier
|
||||||
|
|
||||||
|
**`/api/sync/poll` config response** — already returns `feature_flags` (Phase 5)
|
||||||
|
- Enhance: merge tier-based flags with `school.feature_overrides`
|
||||||
|
- Add `suspension_reason: str | None` to config (e.g. "overdue_invoice")
|
||||||
|
|
||||||
|
**New endpoint: `PUT /api/schools/{id}/feature-overrides`**
|
||||||
|
- Super admin sets per-school feature flag overrides
|
||||||
|
- Validates keys against allowed feature set
|
||||||
|
|
||||||
|
**License validate response** — also returns merged feature flags (already does, enhance)
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
**SchoolDetailPage.vue** — Feature Flags panel (super admin only)
|
||||||
|
- Shows current effective flags (tier base + overrides)
|
||||||
|
- Toggle switches for each overrideable feature:
|
||||||
|
- `api_keys`, `webhooks`, `bulk_enrollment`, `beta_reports`, `multi_terminal`
|
||||||
|
- Save button → calls PUT /api/schools/{id}/feature-overrides
|
||||||
|
- Clear overrides button
|
||||||
|
|
||||||
|
**On-prem side** (documented for reference)
|
||||||
|
- `sync/poll` response: `suspended: true` + `suspension_reason`
|
||||||
|
- On-prem shows banner: "Account suspended — {reason}. SMS disabled."
|
||||||
|
- Feature flags stored in Redis with 5-min TTL for fast checks
|
||||||
|
|
||||||
|
## Key Files to Create/Modify
|
||||||
|
|
||||||
|
- `backend/app/models/school.py` — add `feature_overrides` JSON column
|
||||||
|
- `backend/app/routers/schools.py` — add PUT /{id}/feature-overrides endpoint
|
||||||
|
- `backend/app/routers/sync.py` — merge feature_overrides into poll config
|
||||||
|
- `backend/migrations/versions/005_feature_overrides.py` (new)
|
||||||
|
- `frontend/src/pages/SchoolDetailPage.vue` — feature flags panel
|
||||||
|
- `frontend/src/lib/api.ts` — updateFeatureOverrides(id, data)
|
||||||
|
|||||||
@@ -1,9 +1,66 @@
|
|||||||
# Phase 14: Onboarding Wizard
|
# Phase 14: Onboarding Wizard + Welcome Email
|
||||||
|
|
||||||
**Status:** Not started
|
**Status:** Not started
|
||||||
|
**Depends on:** Phase 9 (Email — welcome email template)
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Welcome email with license key; onboarding checklist UI in SchoolDetailPage.
|
|
||||||
|
|
||||||
## Plans
|
When a new school is registered, automatically send a welcome email with the license key
|
||||||
- [ ] TBD — run /paul:plan when Phase 13 is complete
|
and setup instructions. Super admin has a visual onboarding checklist in SchoolDetailPage
|
||||||
|
to track setup completion and activate the school.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
**Welcome email auto-send on school creation**
|
||||||
|
- `POST /api/schools` — after school + license are created, fire `onboarding.send_welcome_email` task
|
||||||
|
- Template `welcome.html` — school name, license key (formatted), setup URL, portal login URL
|
||||||
|
|
||||||
|
**New Celery task: `onboarding.send_welcome_email(school_id)`**
|
||||||
|
- Renders `welcome.html` with school + license data
|
||||||
|
- Sends to `school.billing_email` or `school.contact_email`
|
||||||
|
- Logs to `email_logs` (Phase 9)
|
||||||
|
|
||||||
|
**`onboarding_completed_at` column on School**
|
||||||
|
- Set when super admin clicks "Complete Onboarding" / "Activate School"
|
||||||
|
|
||||||
|
**New endpoint: `POST /api/schools/{id}/activate`**
|
||||||
|
- Sets `school.status = active` + `school.onboarding_completed_at = now()`
|
||||||
|
- Validates that license exists and billing plan is set
|
||||||
|
|
||||||
|
**"Resend welcome email" endpoint**
|
||||||
|
- `POST /api/schools/{id}/resend-welcome` — re-fires the welcome email task
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
**SchoolDetailPage.vue** — Onboarding Checklist panel
|
||||||
|
- Visible when `school.status == pending`
|
||||||
|
- Steps with checkmarks:
|
||||||
|
1. School created ✓ (always done)
|
||||||
|
2. License issued (check if license exists)
|
||||||
|
3. Billing plan set (check if subscription exists)
|
||||||
|
4. SMS credits added (check if sms_credits > 0)
|
||||||
|
5. Welcome email sent (check if email_logs has a welcome email for this school)
|
||||||
|
6. School admin account created (check if any school_admin users linked)
|
||||||
|
- "Resend Welcome Email" button
|
||||||
|
- "Activate School" button (calls POST /api/schools/{id}/activate)
|
||||||
|
- Disabled until steps 1–3 are complete
|
||||||
|
|
||||||
|
**welcome.html** Jinja2 email template
|
||||||
|
- School name, license key (large monospace display)
|
||||||
|
- Step-by-step setup instructions
|
||||||
|
- Link to Hub portal: `{HUB_BASE_URL}/login`
|
||||||
|
- Contact support link
|
||||||
|
|
||||||
|
## Key Files to Create/Modify
|
||||||
|
|
||||||
|
- `backend/app/models/school.py` — add `onboarding_completed_at` column
|
||||||
|
- `backend/app/tasks/onboarding.py` (new) — send_welcome_email task
|
||||||
|
- `backend/app/worker.py` — include onboarding tasks
|
||||||
|
- `backend/app/routers/schools.py` — auto-trigger welcome email on create,
|
||||||
|
add POST /{id}/activate, POST /{id}/resend-welcome
|
||||||
|
- `backend/app/templates/email/welcome.html` (new)
|
||||||
|
- `backend/migrations/versions/006_onboarding_fields.py` (new)
|
||||||
|
- `frontend/src/pages/SchoolDetailPage.vue` — onboarding checklist panel
|
||||||
|
- `frontend/src/lib/api.ts` — activateSchool, resendWelcomeEmail
|
||||||
|
|||||||
@@ -1,9 +1,88 @@
|
|||||||
# Phase 15: UX Polish + Ops Tools
|
# Phase 15: UX Polish + Super Admin Ops Tools
|
||||||
|
|
||||||
**Status:** Not started
|
**Status:** Not started
|
||||||
|
**Depends on:** All previous phases
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Audit log viewer, global search, bulk export, mobile responsive portal, production hardening.
|
|
||||||
|
|
||||||
## Plans
|
Production-ready polish: audit log viewer, global search, bulk operations, mobile-responsive
|
||||||
- [ ] TBD — run /paul:plan when Phase 14 is complete
|
portal, keyboard shortcuts, error boundaries, and production security hardening.
|
||||||
|
|
||||||
|
## Planned Scope
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
**Audit log viewer endpoint**
|
||||||
|
- `GET /api/audit-logs` — paginated, filterable by school, action, actor, date range
|
||||||
|
- Already have `audit_logs` table — just need the endpoint + frontend
|
||||||
|
|
||||||
|
**Global search endpoint**
|
||||||
|
- `GET /api/search?q={query}` — searches across schools, invoices, tickets
|
||||||
|
- Returns grouped results: `{ schools: [...], invoices: [...], tickets: [...] }`
|
||||||
|
- Limit 5 results per category
|
||||||
|
|
||||||
|
**Bulk school actions**
|
||||||
|
- `POST /api/schools/export-csv` — export school list as CSV (name, status, tier, credits, license)
|
||||||
|
- `POST /api/schools/bulk-status` — change status for multiple school IDs (super admin)
|
||||||
|
|
||||||
|
**Rate limiting on auth endpoints**
|
||||||
|
- Add `slowapi` middleware: `POST /auth/login` — max 10 req/min per IP
|
||||||
|
- Return `429 Too Many Requests` with Retry-After header
|
||||||
|
|
||||||
|
**HTTPS redirect**
|
||||||
|
- Add redirect middleware in `main.py` (X-Forwarded-Proto check)
|
||||||
|
- Update `nginx.conf` to pass X-Forwarded-Proto header
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
**Audit log page** (`AuditLogPage.vue` — new or sub-section in admin)
|
||||||
|
- Table: timestamp, actor, action, entity type, entity name, IP
|
||||||
|
- Filters: date range, actor, action type
|
||||||
|
|
||||||
|
**Global search** (keyboard shortcut `/`)
|
||||||
|
- `SearchModal.vue` — command palette style
|
||||||
|
- Press `/` anywhere → modal opens with search input
|
||||||
|
- Results grouped by type, click navigates to entity
|
||||||
|
|
||||||
|
**Keyboard shortcuts**
|
||||||
|
- `/` — open global search
|
||||||
|
- `N` — new school (when on /schools)
|
||||||
|
- `Esc` — close any open modal
|
||||||
|
|
||||||
|
**Mobile-responsive portal**
|
||||||
|
- PortalSidebar: collapsible on mobile (hamburger menu)
|
||||||
|
- PortalLayout: responsive header
|
||||||
|
- All portal pages: responsive table → card layout on mobile
|
||||||
|
|
||||||
|
**Error boundary component** (`ErrorBoundary.vue`)
|
||||||
|
- Wraps page content in AppLayout/PortalLayout
|
||||||
|
- On uncaught error: shows friendly "Something went wrong" card with retry button
|
||||||
|
|
||||||
|
**Empty state illustrations**
|
||||||
|
- SchoolsPage: "No schools yet" with Add School CTA
|
||||||
|
- TicketsPage: "No tickets" with Submit Ticket CTA
|
||||||
|
- BillingPage: "No invoices yet" with Generate Invoices CTA
|
||||||
|
|
||||||
|
**Dashboard refresh UX**
|
||||||
|
- Auto-refresh every 5 minutes (setInterval)
|
||||||
|
- Show "Data may be stale" banner after 10 minutes without refresh
|
||||||
|
|
||||||
|
**CSV export button** in SchoolsPage toolbar
|
||||||
|
|
||||||
|
## Key Files to Create/Modify
|
||||||
|
|
||||||
|
- `backend/app/routers/search.py` (new)
|
||||||
|
- `backend/app/routers/audit.py` (new — expose existing audit_logs table)
|
||||||
|
- `backend/app/routers/schools.py` — bulk-status + export-csv endpoints
|
||||||
|
- `backend/app/main.py` — include search + audit routers, add rate limit + HTTPS middleware
|
||||||
|
- `backend/requirements.txt` — add `slowapi`
|
||||||
|
- `frontend/src/pages/AuditLogPage.vue` (new)
|
||||||
|
- `frontend/src/components/ui/SearchModal.vue` (new)
|
||||||
|
- `frontend/src/components/ui/ErrorBoundary.vue` (new)
|
||||||
|
- `frontend/src/layouts/AppLayout.vue` — global keyboard shortcut handler
|
||||||
|
- `frontend/src/layouts/PortalLayout.vue` — mobile responsive
|
||||||
|
- `frontend/src/components/sidebar/PortalSidebar.vue` — mobile collapsible
|
||||||
|
- `frontend/src/pages/SchoolsPage.vue` — bulk actions, CSV export, empty state
|
||||||
|
- `frontend/src/router/index.ts` — /audit-logs route
|
||||||
|
- `frontend/src/components/sidebar/AppSidebar.vue` — audit logs nav item
|
||||||
|
- `frontend/src/lib/api.ts` — globalSearch, getAuditLogs, exportSchoolsCsv, bulkSchoolStatus
|
||||||
|
|||||||
Reference in New Issue
Block a user