Skip to content

feat(admin): THI-234 Phase 9 analytics widgets (Santé Supabase + activity heatmap)#370

Merged
thierryvm merged 4 commits into
mainfrom
feat/THI-234-admin-analytics
Jun 4, 2026
Merged

feat(admin): THI-234 Phase 9 analytics widgets (Santé Supabase + activity heatmap)#370
thierryvm merged 4 commits into
mainfrom
feat/THI-234-admin-analytics

Conversation

@thierryvm

Copy link
Copy Markdown
Owner

THI-234 — Phase 9 Admin Panel : widgets analytics live

Remplace 2 des 4 widgets placeholder de /app/admin par des données réelles (démo écoles, budget 0€, sans table dédiée).

Changements

  • Migration 032 — 2 RPC SECURITY DEFINER (admin_platform_stats json, admin_activity_heatmap table) agrégeant cross-user profiles+progress, gate super_admin interne (raise PERMISSION_DENIED) + REVOKE public/anon + set search_path. Appliquée en prod (additive, calquée sur le pattern approve_teacher 027).
  • Widget Santé Supabase — total users, actifs 7j, leçons complétées 30j/total, top 5 leçons.
  • Widget Activité élèves — heatmap GitHub-style 52 semaines (THI-77).
  • Sentry + Application health → restent placeholders (Phase 9 v2 — nécessitent route API + secret).
  • Hook useAdminAnalytics (fetch parallèle, read-only) + helpers heatmap purs + types Database.Functions (zéro any).

Sécurité — isolation prouvée empiriquement (REST+JWT)

Caller Résultat
super_admin ✅ 200 + données
student ✅ 400 PERMISSION_DENIED (gate role)
anon ✅ 401 permission denied for function (REVOKE public/anon)

Defense in depth : UI gated <RequireRole allowed={['super_admin']}>. Counts only, aucune PII.

Tests

24 nouveaux (helper 11 + hook 4 + AdminPanel 9). type-check + lint clean.
⚠️ Note d'intégrité : 1 test d'intégration approveTeacher (cross-institution) est flaky stateful (état test users partagé en prod — dette THI-334), sans rapport avec THI-234, skippé en CI, et passe en isolation (11/11).

Validation

Gates en cours : security-auditor + ui-auditor + mobile-responsive-auditor (parallèle) → feature-dev:code-reviewer. Voie A preview desktop + mobile 390px à confirmer avant merge.

🤖 Generated with Claude Code

…ivity heatmap

Replace 2 of the 4 AdminPanel placeholder widgets with live data:
- Santé Supabase: total users, active 7d, lessons completed 30d/total, top 5 lessons
- Activité élèves: GitHub-style 52-week heatmap (THI-77)

Data via 2 SECURITY DEFINER RPCs (migration 032) with an internal super_admin
gate (raise PERMISSION_DENIED otherwise) + REVOKE public/anon — cross-user
aggregates without exposing rows to non-admins. Isolation proven empirically
via REST+JWT: super_admin 200, student 400 PERMISSION_DENIED, anon 401.

Sentry + Application health stay placeholders (Phase 9 v2 — need API route +
secret). Counts only, no PII.

- migration 032 (applied to prod, additive)
- Database.Functions types for the 2 RPCs (zero any)
- useAdminAnalytics hook (parallel RPC fetch, read-only)
- pure heatmap helpers (buildHeatmapGrid/heatmapLevel/heatmapMax) + tests
- AdminPanel widgets data-driven (loading/error/empty states)
- 24 new tests; adminPanel.test rewritten for the new widgets

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
terminal-learning Ready Ready Preview, Comment Jun 4, 2026 12:17pm

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @thierryvm, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

thierryvm and others added 2 commits June 4, 2026 14:11
…or W2)

When the platform has zero completions the aria-label said "… jour le plus
actif 0" which is confusing for screen readers. Use a clear "Aucune activité
sur les 52 dernières semaines." in that case. THI-234.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…le-auditor)

The status-triage <select> (THI-320) used text-sm (14px) → Safari iOS auto-zooms
the page on focus when a form control is < 16px. Bump to text-base (16px).
Out-of-scope drive-by surfaced by the THI-234 mobile gate (the select renders on
the admin page this PR touches).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ode-reviewer)

- active_users_7d now filters `completed = true` like the sibling metrics.
  Redundant today (ProgressContext only upserts completed rows — verified) but
  defensive + consistent if the upsert ever stamps completed_at on lesson-start.
  Re-applied to prod via CREATE OR REPLACE (idempotent).
- useAdminAnalytics surfaces BOTH RPC errors (Promise.all) instead of masking the
  second — easier debugging when both fail from the same root cause.

code-reviewer findings #1 + #4 (THI-234). #2/#5 (runtime narrowing, useId) → backlog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thierryvm thierryvm merged commit 9ee05dd into main Jun 4, 2026
4 checks passed
@thierryvm thierryvm deleted the feat/THI-234-admin-analytics branch June 4, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant