Projects BibleWeb Navigation UX Admin dashboard
Done

Admin dashboard

Area: Navigation UX

1. Context

The admin dashboard gives the operator visibility into AI chat usage and costs without needing to query the database directly. It is scoped to the "Ask AI" feature and provides operational awareness: how many questions are being asked, what it's costing, and who the heaviest users are.

2. Functional

The dashboard loads four data sets from the aiChatLogs table:

  1. Summary cards — aggregate totals: total questions, total cost (USD), average response time (ms), average tokens per query (input + output).
  2. Daily questions chart — question count per day for the last 30 days, rendered as a horizontal bar chart.
  3. Daily cost chart — USD cost per day for the last 30 days, rendered as a horizontal bar chart.
  4. Top users — top 10 users by question count, with their total cost. Anonymous users shown as (anonymous).
  5. Recent conversations — last 50 log entries. Each row is clickable to expand a detail panel showing the full question, full answer, model used, RAG verse count, RAG context token count, language, and conversation ID.

Access is gated: isAdmin(locals.session) must return true or a 403 is thrown.

3. UX & Design

  • Page max-width: 72rem, 1.5rem padding.
  • Stat cards: 4-column grid (collapses to 2 on ≤900px, 1 on ≤500px). Large bold number, small label below.
  • Bar charts: each day is a row with a date label (monospace, right-aligned, 6.25rem wide), a proportional fill bar, and a numeric value. Bars scale relative to the max value in that dataset. Questions bar uses --color-cross-ref; cost bar uses --color-jesus-words (gold).
  • Conversations table: horizontally scrollable, alternating row backgrounds, clickable rows with aria-expanded for accessibility. Expanded detail row shows question and answer in a pre-wrap text block with metadata badges below.
  • All values are formatted: costs as $0.0000 (4 decimal places for small values, 2 for ≥$1), durations as Xms or X.Xs, timestamps as DD Mon HH:MM.

4. Technical

Route: apps/web/src/routes/(app)/admin/+page.svelte + +page.server.ts

Server load (+page.server.ts):

  • Auth check: isAdmin(locals.session) from $lib/server/admin.
  • Four Drizzle queries against aiChatLogs (imported from @bibleweb/db):
    • count, sum, avg aggregate for summary.
    • Group-by-date with substr(..., 1, 10) for daily stats, filtered to last 30 days.
    • limit(50) ordered by desc(aiChatLogs.id) for recent conversations.
    • Group-by-userId ordered by desc(count(...)), limit(10) for top users.
  • All sum/avg results cast to Number() (Drizzle returns them as strings from SQLite).

Client component (+page.svelte):

  • Svelte 5 runes; expandedRows is a $state<Set<number>>.
  • Bar widths computed as (value / max) * 100% with a minimum 2px fill for visibility.
  • $derived values for maxDailyQuestions and maxDailyCost guard against division-by-zero with fallback minimums.

Dependencies:

  • @bibleweb/dbaiChatLogs Drizzle table schema
  • $lib/server/adminisAdmin() helper
  • drizzle-ormcount, sum, avg, sql, desc

5. Status

Fully implemented. Data queries, access guard, summary cards, bar charts, and expandable conversation table are all working. Dashboard is scoped only to AI chat logs; expanding it to cover other metrics (notes, users, feedback) would require additional queries and sections.