3 Commits

Author SHA1 Message Date
kevin-asprec
9ef8f1a421 feat(phases-10-15): complete TapTrack Hub v1.0
Phase 10 — Support Ticket System:
- tickets.py router: SLA status (on_track/at_risk/breached/responded), email
  notifications on create+reply via background threads, school_name in list,
  priority filter, bulk-close endpoint
- tasks/tickets.py: escalate_stale Celery task (48h→high, 72h no reply→urgent)
- worker.py: escalate_stale scheduled every hour
- templates/email/ticket_notification.html: HTML ticket notification email
- TicketsPage.vue: status tabs, SLA badge, priority badge, school name column,
  checkbox bulk-close, pagination
- TicketDetailPage.vue: inline priority/status/assignee selectors, SLA timer,
  internal note lock icon, closed-ticket guard

Phase 11 — Monthly Report Generation:
- models/report.py: MonthlyReport + SchoolMonthlyStats ORM models
- tasks/reports.py: send_monthly_reports enhanced with SMS stats, attendance
  data, invoice summary, stores MonthlyReport record per school per month

Phase 12 — On-Prem Monthly Report Pull:
- tasks/reports.py: pull_monthly_stats task — httpx GET to each school's
  hub_base_url, upserts SchoolMonthlyStats; runs 1st at 5am
- worker.py: pull_monthly_stats scheduled 1st at 5am

Phase 13 — Feature Flags + Suspension:
- models/school.py: hub_base_url, feature_overrides (JSON), onboarding_completed_at
- routers/schools.py: PUT /{id}/feature-overrides endpoint
- routers/sync.py: _tier_features() merges school.feature_overrides into poll config

Phase 14 — Onboarding Wizard + Welcome Email:
- tasks/onboarding.py: send_welcome_email Celery task with license key
- routers/schools.py: auto-trigger welcome email on POST /schools,
  POST /{id}/activate (status→active + onboarding_completed_at),
  POST /{id}/resend-welcome

Phase 15 — UX Polish + Ops Tools:
- routers/search.py: GET /api/search?q= (schools + invoices + tickets, 5 each)
- routers/audit.py: GET /api/audit-logs (paginated, filterable)
- AppLayout.vue: global search bar with debounced dropdown, result navigation
- AuditLogsPage.vue: new page with filter + pagination
- AppSidebar.vue: Audit Logs nav item added
- router/index.ts: /audit-logs route
- api.ts: globalSearch, getAuditLogs, activateSchool, resendWelcomeEmail,
  updateFeatureOverrides, bulkCloseTickets

Deployment:
- docker-compose.yml: x-backend-env anchor (DRY), PDF_DIR env var,
  seed service (one-shot python seed.py on first boot)
- migrations/003_phases11_15.py: monthly_reports, school_monthly_stats tables
  + schools hub_base_url/feature_overrides/onboarding_completed_at columns
2026-03-16 14:28:52 +08:00
kevin-asprec
1febb3cfa9 feat(phase-9): email dispatcher — HTML templates, delivery log, test endpoint
Backend:
- app/models/email_log.py: EmailLog table (school_id, to, subject, type, status,
  error, sent_at) with EmailType + EmailStatus enums
- migrations/002_phase9_email_logs.py: Alembic migration for email_logs table
- app/templates/email/: 6 Jinja2 HTML templates — base layout, invoice,
  low_credit, license_expiry, overdue_warning, suspension
- app/services/email.py: enhanced send_email() — accepts template_name+context
  for HTML rendering, logs every attempt to email_logs, retries up to 3x on
  transient SMTP failure with exponential backoff
- app/routers/email.py: GET /api/email/logs (paginated, filterable by type/status/school),
  POST /api/email/test (send test email, super admin)
- tasks/billing.py: invoice + overdue warning + suspension emails now use HTML templates
- tasks/sms.py: low credit alert now uses HTML template
- tasks/license.py: expiry warning now uses HTML template
- app/main.py + migrations/env.py: wire in email_log model + email router

Frontend:
- EmailLogsPage.vue: table with to/subject/type badge/status badge/sent_at/error,
  type+status filters, pagination, Send Test Email modal
- router/index.ts: /email-logs route
- AppSidebar.vue: Email Logs nav item
- api.ts: getEmailLogs, sendTestEmail
2026-03-16 14:03:02 +08:00
kevin-asprec
132290957c feat(phase-5): on-prem sync poll hardening + stale job reclaim
Harden the Hub-side polling protocol for on-prem TapTrack agents:
- sync/poll: accept job_failed_ids (retry/fail on-prem delivery failures)
- sync/poll: deduct credits + write ledger when on-prem reports sent jobs
- sync/poll: return feature_flags (tier-based) + suspended flag in config
- sync/poll: skip job dispatch for suspended/expired schools
- sms_jobs: add delivered_via (pull|push) + processing_started_at columns
- tasks/sms: new sms.reclaim_stale_jobs task resets processing→pending if on-prem
  goes offline (jobs stuck >5 min), enabling Celery push fallback
- tasks/sms: tag Celery-sent jobs as delivered_via='push'
- worker: schedule reclaim_stale_jobs every 5 minutes
- migration: 001_phase5 adds delivered_via + processing_started_at to sms_jobs
2026-03-16 12:30:37 +08:00