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:
@@ -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
|
||||
|
||||
**Status:** Not started
|
||||
**Depends on:** Phase 9 (Email Dispatcher — for email notifications)
|
||||
|
||||
## Goal
|
||||
Email notifications on tickets, SLA tracking, internal notes, priority escalation.
|
||||
|
||||
## Plans
|
||||
- [ ] TBD — run /paul:plan when Phase 9 is complete
|
||||
Polish and harden the existing ticket system with SLA tracking display, email notifications
|
||||
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
|
||||
**Depends on:** Phase 9 (Email), Phase 12 (On-Prem Data Pull)
|
||||
|
||||
## Goal
|
||||
Auto monthly report email to schools with attendance summary and SMS usage.
|
||||
|
||||
## Plans
|
||||
- [ ] TBD — run /paul:plan when Phase 10 is complete
|
||||
On the 1st of each month, automatically generate and email a comprehensive report to each
|
||||
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
|
||||
|
||||
**Status:** Not started
|
||||
**Depends on:** Phase 11 (Monthly Reports)
|
||||
|
||||
## Goal
|
||||
Hub pulls attendance data from each TapTrack instance for monthly reports.
|
||||
|
||||
## Plans
|
||||
- [ ] TBD — run /paul:plan when Phase 11 is complete
|
||||
Hub pulls attendance summary data from each on-prem TapTrack instance on the 1st of the month
|
||||
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
|
||||
**Depends on:** Phase 3 (License Validation), Phase 8 (Billing / Overdue Suspension)
|
||||
|
||||
## Goal
|
||||
Per-school feature flag overrides; suspension propagation to on-prem.
|
||||
|
||||
## Plans
|
||||
- [ ] TBD — run /paul:plan when Phase 12 is complete
|
||||
Hub controls which features each school's on-prem can use via tier-based + per-school
|
||||
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
|
||||
**Depends on:** Phase 9 (Email — welcome email template)
|
||||
|
||||
## Goal
|
||||
Welcome email with license key; onboarding checklist UI in SchoolDetailPage.
|
||||
|
||||
## Plans
|
||||
- [ ] TBD — run /paul:plan when Phase 13 is complete
|
||||
When a new school is registered, automatically send a welcome email with the license key
|
||||
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
|
||||
**Depends on:** All previous phases
|
||||
|
||||
## Goal
|
||||
Audit log viewer, global search, bulk export, mobile responsive portal, production hardening.
|
||||
|
||||
## Plans
|
||||
- [ ] TBD — run /paul:plan when Phase 14 is complete
|
||||
Production-ready polish: audit log viewer, global search, bulk operations, mobile-responsive
|
||||
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