Skip to content

Dashboard

Purpose

The Dashboard is the operator's at-a-glance view of platform health: KPIs (registrations, FTDs, deposits, withdrawals, GGR/NGR, ATPU/ARPU), turnover by game type, and finance reconciliation. It is the single screen leadership and operations look at first thing in the morning to see whether yesterday went sideways. Evospin ships two implementations living side by side — the older v1 dashboard/ controller and the v2 dashboard-v2/ controller — and the admin-fe wires both.

Audience

Operations, finance, leadership. Engineering uses it secondarily as a "did our deploy break GGR" smoke test.

Path in admin-fe

Tab URL Page
Overview (landing) / ebit-admin-fe/src/app/(dashboard)/page.tsxcomponents/pages/overview/index.tsx
Dashboard (general) /dashboard ebit-admin-fe/src/app/(dashboard)/dashboard/page.tsx
Dashboard / Games /dashboard-games ebit-admin-fe/src/app/(dashboard)/dashboard-games/page.tsx
Dashboard / Game by Slug /dashboard-game-by-slug-stats ebit-admin-fe/src/app/(dashboard)/dashboard-game-by-slug-stats/page.tsx
Dashboard / Finances /dashboard/finances ebit-admin-fe/src/app/(dashboard)/dashboard/finances/page.tsx
Games chart (in-house) /games-chart ebit-admin-fe/src/app/(dashboard)/games-chart/page.tsx

Tab routing: components/common/DashboardTabs/index.tsx reads usePathname() to highlight the active tab.

Backing API endpoints

Endpoint Source
GET /admin/dashboard/quick-stats apps/api/src/dashboard/admin.dashboard.controller.ts:27
GET /admin/dashboard/finance-tab apps/api/src/dashboard/admin.dashboard.controller.ts:33
GET /admin/dashboard/games-stats apps/api/src/dashboard/admin.dashboard.controller.ts:41
GET /admin/dashboard/games-charts apps/api/src/dashboard/admin.dashboard.controller.ts:49
GET /admin/dashboard/affiliate-stats apps/api/src/dashboard/admin.dashboard.controller.ts:57
GET /admin/dashboard/users-stats apps/api/src/dashboard/admin.dashboard.controller.ts:64
GET /admin/dashboard/providers-stats apps/api/src/dashboard/admin.dashboard.controller.ts:71
GET /admin/dashboard-v2/main/stats apps/api/src/dashboard-v2/admin.dashboard-combined.controller.ts:27
GET /admin/dashboard-v2/game/stats apps/api/src/dashboard-v2/admin.dashboard-combined.controller.ts:34
GET /admin/dashboard-v2/main/options apps/api/src/dashboard-v2/admin.dashboard-combined.controller.ts:41
GET /admin/dashboard-v2/query/games-by-slug apps/api/src/dashboard-v2/admin.dashboard-separated.controller.ts:27
GET /admin/dashboard-v2/query/games-by-type apps/api/src/dashboard-v2/admin.dashboard-separated.controller.ts:34
GET /admin/dashboard-v2/query/bets-by-type apps/api/src/dashboard-v2/admin.dashboard-separated.controller.ts:41
GET /admin/dashboard-v2/query/payments apps/api/src/dashboard-v2/admin.dashboard-separated.controller.ts:48
GET /admin/dashboard-v2/query/registrations apps/api/src/dashboard-v2/admin.dashboard-separated.controller.ts:55

Frontend wiring: ebit-admin-fe/src/queries/dashboard/index.ts, queries/overview/, queries/finances/.

Key actions

Action Required role API call DB / source Audit-logged?
View KPIs (reg, FTD, deposit, withdraw) dashboard.view permission GET /admin/dashboard/quick-stats aggregations over User, Withdraw, Deposit, Bet yes (read-through AdminActionLog)
View finance tab (reconciliation) dashboard.view GET /admin/dashboard/finance-tab Vault*, Accounting* tables yes
Drill into per-game stats dashboard.view GET /admin/dashboard-v2/query/games-by-slug?slug=... Bet, Game yes
Drill into in-house RTP charts dashboard.view GET /admin/dashboard/games-charts Bet (house games) yes
Switch date range / granularity n/a (UI-only) passes startDate, endDate, granularity query params - no

AdminActionLog rows are written per request via the action-log middleware; see data-model/ for the full schema (AdminActionLog lives in libs/_prisma/src/schema/api.prisma:1479-1497).

Filters and views

  • Date range selector (today, last 24h, custom). Posted as startDate / endDate (ISO).
  • Granularity (hour / day). Drives the groupBy clause server-side.
  • Game type filter on Games tab (slots / house games / sportbook).
  • Per-graph visibility toggles in components/pages/dashboard/Filters/.
  • Currency context is always USD-equivalent — see getUserUsdBalanceQuery and the admin/accounting/usd-balance/... endpoints. There is no per-currency dashboard breakdown.

Common workflows

  1. Morning health check. Operator opens /, scans Overview cards (OverviewCards), then jumps to /dashboard for time-series. Looks for: anomalous deposit-vs-withdraw ratio, NGR drift, registration spike (bot signal).
  2. Spike investigation. Spike on Reg Count. Operator opens /dashboard → users-stats → cross-references with /admin-logs (see admin-logs.md) to see if a promo code drove it. If not, escalate to bots — see bots.md.
  3. Finance reconciliation. Finance opens /dashboard/finances for vault balances and net flow per currency. They cross-check against the live accounting endpoint at GET /admin/accounting/balances (see api-reference/bo.md).
  4. Per-game RTP audit. RTP looks "off" on Plinko. Operator opens /games-chart, picks Plinko, narrows date range. Calls /admin/dashboard/games-charts server-side; compares to provably-fair seed validation in flows/.
  5. Provider performance. Operator opens /games/stats/providers. Views GET /admin/dashboard/providers-stats to compare BGaming vs PM8 vs ST8 vs evogames revenue + margin.

Edge cases / gotchas

  • Two dashboard backends. v1 (/admin/dashboard/...) is in maintenance mode; v2 (/admin/dashboard-v2/...) is the new home. The admin-fe still calls both. If a metric disagrees across tabs, that's why — they query slightly different aggregations. Tracked in repo issues; do not "fix" by editing v1.
  • Online-count is inflated. Any "online users" widget on Overview is sourced from the UsersOnlineUpdated socket event, which adds a fakeUserOnline floor of 180-500. Per-screen counters are intentionally padded — see project_online_count_inflation memory.
  • Day boundaries are UTC. Granularity is computed against UTC, not browser-local. A 23:30 EST bet lands in tomorrow's bucket on the chart.
  • No per-locale segmentation. No EU / UK / RoW split surfaces here. For that, query admin/country (api-reference/bo.md).
  • Data lag. Dashboard reads BullMQ-aggregated rollups for some stats (user-stats-migration queue); a fresh deposit may take up to 60 s to surface in users-stats.
  • Runbook for stale dashboard data: runbooks/ → "dashboard not updating"
  • ADR on the v1 → v2 split: adr/
  • Related flow: flows/ → bet-place / deposit / withdraw (the screens that feed these aggregates)
  • For per-user rollups (not platform-wide): see user-profile.md
  • The Overview card definitions: ebit-admin-fe/src/components/pages/overview/const/

Sequence — opening the Overview tab

sequenceDiagram
    actor admin
    participant admin-fe
    participant api
    participant pg as Postgres
    admin->>admin-fe: GET /
    admin-fe->>admin-fe: middleware.ts checks JWT + MFA cookie
    admin-fe->>api: GET /admin/dashboard/quick-stats?startDate=...&endDate=...
    api->>api: PermissionGuard('dashboard.view')
    api->>pg: aggregate User/Bet/Deposit/Withdraw
    pg-->>api: rows
    api-->>admin-fe: KPI payload
    admin-fe->>api: GET /admin/dashboard-v2/query/registrations?...
    api->>pg: time-series aggregate
    pg-->>api: bucketed rows
    api-->>admin-fe: chart series
    admin-fe-->>admin: rendered cards + charts