Skip to content

feat: real-time analytics & BI platform (issue #44) - #70

Merged
josueazc merged 23 commits into
Velar-Bonds:mainfrom
KevinLatino:feat/analytics-bi-dashboard
Jul 29, 2026
Merged

feat: real-time analytics & BI platform (issue #44)#70
josueazc merged 23 commits into
Velar-Bonds:mainfrom
KevinLatino:feat/analytics-bi-dashboard

Conversation

@KevinLatino

@KevinLatino KevinLatino commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary Closes #44

Closes #44 — real-time analytics & BI platform: a pure aggregation engine over bonds/transfers/reports, time-series/trend analysis, per-party/per-country breakdowns with drill-down, saved views, threshold alerting, scheduled-report generation, and CSV/PDF export, with RBAC and an accessible dashboard UI.

Architecture mirrors the provenance engine: a pure, fixture-tested engine (apps/api/src/analytics/engine/) with zero I/O, isolated Supabase access in AnalyticsDataService, and RBAC scoping resolved before the engine ever runs (TSE/admin see everything, emisor sees only their own party, no other role has aggregate-analytics access).

  • Engine: aggregations, transfer funnel, time-series (day/week/month bucketing), trend helpers (delta/moving-average/top-N/threshold), compliance (reuses computeComplianceForPeriods, doesn't reimplement it), and dot-path threshold alerting.
  • Backend: GET /analytics/snapshot, snapshot-based CSV/PDF export (pdf-lib), saved-views CRUD, alert-rule CRUD + evaluation (emits notifications via the existing NotificationsService), and a manual scheduled-report trigger behind an interface+stub (no cron/vendor). Legacy bond-detail drill-down routes are kept working unchanged.
  • Migration: analytics_saved_views + analytics_alert_rules, additive only.
  • Frontend: new BI dashboard (/tse/analytics rewritten, new /partido/analytics) — KPIs, 5 chart types (Recharts, the frontend's first third-party UI dependency), filter bar, drill-down, saved views, CSV/PDF export. Every chart ships an accessible <table> fallback + aria-label.
  • Along the way: found and fixed a pre-existing design-system bug where several color tokens (secondary, tertiary, warning, etc.) were silently dropped from the compiled CSS by Tailwind's unused-theme-key pruning, since nothing had exercised them via colorVar() before this PR's charts.

Test plan

  • npx jest in apps/api — 238 tests (engine is pure/fixture-driven; service/controller tests mock SupabaseService/NotificationsService and prove RBAC, alert emission, and export-format validation)
  • npx jest in apps/web — 41 tests
  • npm run build and npx tsc --noEmit pass in both workspaces, no live VELAR credentials required
  • npx eslint . — 0 errors in both workspaces
  • Manually verified in a real browser (Playwright) against the fixture dataset: all KPIs/charts/funnel/compliance numbers match the engine's own unit tests; dark theme contrast and Recharts tooltip/hover styling checked and fixed

AnalyticsInput/Query/Scope/Snapshot and every breakdown/series/trend
shape, plus AlertRule/AlertBreach, SavedView and ScheduledReportConfig,
for issue Velar-Bonds#44's analytics & BI platform.
Three parties across two countries, bonds spanning several statuses,
transfers covering the full transfer funnel, and reports spanning
on-time/late/missing compliance outcomes — same discipline as
fixtures/provenance.ts, so the engine and API can be built and tested
with no VELAR database or credentials.
New NotificationType.ANALYTICS_THRESHOLD_BREACHED, delivered through
the existing NotificationsService.emit() when an alert rule breaches.
Updates NotificationBell's exhaustive label map accordingly.
aggregateByBondStatus/Party/Country and aggregateValueVolume, plus
computeTransferFunnel (reusing TRANSFER_LIFECYCLE_STEPS/
TERMINAL_TRANSFER_STATUSES from the provenance work instead of
redefining them). No I/O, no Supabase, no Role — same discipline as
provenance-engine.ts.
UTC-based day/week/month bucketing (issuance, transfer, and
escrow-resolution series) plus generic trend helpers: period-over-period
delta, moving average, top-N, and simple threshold anomaly detection.
compliance.ts adapts the existing computeComplianceForPeriods
(reports/domain/deadlines.ts) instead of reimplementing deadline logic.
alerts.ts evaluates dot-path metric rules against a snapshot, pure and
I/O-free. index.ts composes everything into buildAnalyticsSnapshot,
applying RBAC scope and query filters without ever importing Role.
Fixture-driven, including empty/single-item/off-path edge cases.
Timeseries/trends use small hand-crafted datasets for exact,
hand-verifiable day/week/month bucketing; compliance and alerts use the
shared fixture.
Covers applyScope/applyQueryFilters and buildAnalyticsSnapshot end to
end over the fixture, including scoped/unscoped and empty-dataset cases,
and asserts the input is never mutated.
The only place in the analytics module that touches
SupabaseService.admin.from(...); maps bonds/transfers/reports rows to
the @velar/types shapes the pure engine consumes, and filters out
legacy free-text reports with no period_year/period_month.
Pure renderSnapshotCsv(snapshot) — fully fixture-testable, unlike the
legacy exportTransfersCsv which needs live-joined Supabase rows for
seller/buyer names (kept separately for drill-down compatibility).
renderAnalyticsPdf(snapshot) — pure Node, no headless browser. PDF
internal object ordering isn't byte-stable across runs, so tests are
structural (valid %PDF- header, re-parseable, expected page count)
rather than byte-exact snapshots.
ScheduledReportGenerator + SCHEDULED_REPORT_GENERATOR DI token, same
interface+stub discipline as the antivirus hook in
reports/files/file-scanner.ts. ManualScheduledReportGenerator produces
a CSV/PDF summary from the current snapshot on demand — no cron, no
vendor.
New tables analytics_saved_views (RLS: owner) and analytics_alert_rules
(RLS: tse/admin), reusing the existing set_updated_at() trigger and
public.auth_role() helper. Additive only — no existing migration is
touched.
resolveScope: TSE/admin see every party, emisor sees only their own
party, every other role is forbidden from aggregate analytics. Adds
GET /analytics/snapshot, snapshot-based CSV/PDF export, saved-views and
alert-rule CRUD (@roles('tse','admin') on privileged config routes),
and manual scheduled-report trigger. Legacy bond-detail drill-down
routes (price-history/owners/top-bonds) and the named-transfer CSV
export are kept working unchanged, moved to /legacy-export.
Service tests cover scope resolution per role, CSV/PDF export scoping,
alert evaluation + notification emission, and the scheduled-report
stub. Controller tests cover the legacy/new export routes and prove
@roles('tse','admin') actually blocks a non-privileged role on the
alert-rules routes.
secondary/secondaryContainer/secondaryFixed/tertiary/tertiaryContainer/
primaryFixedDim were declared in tokens.ts and @theme but silently
stripped from the compiled CSS by Tailwind v4 (unused-theme-key
pruning) since no utility class referenced them — colorVar() resolved
them to an empty string, rendering black/invalid in charts. warning was
missing entirely. Re-declared in the plain :root/[data-theme='dark']
blocks, which Tailwind never prunes.
First third-party UI dependency in the frontend's design system,
chosen over hand-rolling 5 chart types. BarChart/LineChart/AreaChart/
PieChart/StackedBarChart, all wrapped in ChartFrame: role="img" +
aria-label, a screen-reader-only <table> fallback with the same data,
and a shared chartTooltipStyle/cursor so Recharts' hardcoded-white
defaults follow the theme instead.
Lockfile reflects both new dependencies added on this branch
(pdf-lib in apps/api, recharts in apps/web).
…ponents

lib/analytics/{query,client}.ts: pure AnalyticsQuery <-> URLSearchParams
(de)serialization (also the shape a saved view stores) and a thin
wrapper over apiFetch/apiDownload — no parallel HTTP client.
components/analytics/: KpiCard, FilterBar (date range/country/party/
status/bucket), SavedViewsMenu, ExportButtons, DrillDownPanel (reuses
the legacy price-history/owners endpoints for real drill-down), and
AnalyticsDashboard composing all of it — shared by both role pages.
Rewrites /tse/analytics in place (KPIs, filters, all 5 chart types,
funnel, top-bond drill-down, compliance) and adds /partido/analytics
(same component tree, party-scoped by the backend — no party/country
filter shown since it's always the caller's own party). Adds the
"Análisis" nav link to PartidoShell (TSEShell already had one).
BACKEND.md §10: engine file map, RBAC/data-access design, endpoints,
alerting/scheduled-report stub, exports, migration, types, and how to
verify locally with no credentials. FRONTEND_GUIDE.md §15: dashboard
components, integration, and a colorVar() note for chart colors.
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

@KevinLatino is attempting to deploy a commit to the josueazc's projects Team on Vercel.

A member of the Team first needs to authorize it.

@josueazc
josueazc merged commit e416a54 into Velar-Bonds:main Jul 29, 2026
4 of 5 checks passed
josueazc added a commit to mmongee/Velar that referenced this pull request Jul 29, 2026
Resuelve conflictos con main (analytics & BI dashboard, PR Velar-Bonds#70):
- apps/api/package.json: conserva pdfkit (exports de reportes) y pdf-lib
  (PDF de analytics); ambas librerías se usan en módulos distintos.
- packages/types/types/*.d.ts: regenerados con tsc desde el src ya
  fusionado (ReportStatus incluye 'revisado' y
  'pendiente_segunda_aprobacion').
- package-lock.json: regenerado con npm install (pdfkit, @types/pdfkit,
  fast-check + deps de analytics de main).

Verificado: typecheck de api OK; jest src/reports (126) y src/analytics (97) en verde.
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.

Real-time analytics & BI platform (aggregation engine, trends, drill-down, alerts, exports) — full-stack EPIC

2 participants