# Story 3.8: Admin Console — Complete Management Dashboard > **Epic:** 3 — The SaaS Commercial Engine > **Priority:** High > **Status:** ready-for-dev > **Depends on:** 3.1 (quota tracking), 3.6 (Stripe tiers), 3.7 (billing UX) > **Blocks:** — > **Related:** FR13 (real-time quota indicator), FR21 (audit logging), FR18 (fallback rules) --- ## Problem Statement The admin console exists but is a **hollow shell**: - **Dashboard**: 3 of 4 metrics are hardcoded strings ("24", "1,234", "856"). Trends are fake. "Recent Activity" is a placeholder div. - **Users page**: No pagination, no search, no edit, no subscription info. Shows only name/email/role/date. Cannot see what plan a user is on, how much they consume, or manage their subscription. - **No billing/subscription visibility**: The `Subscription` model has real data (tier, status, Stripe IDs, period dates) but there is zero admin UI for it. - **No usage analytics**: `UsageLog` collects per-user, per-feature `requestsCount` and `tokensUsed` but nobody can see it. No charts, no breakdown, no per-user drill-down. - **FeatureFlag model unused**: Schema exists (`key`, `enabled`, `tiers`) with zero code references and zero admin UI. - **No i18n**: Dashboard, users, AI pages have hardcoded English/French text. The `admin.*` locale block (260+ keys) only exists in `en.json`. - **Design inconsistency**: Admin pages use raw Tailwind (`text-gray-900`, `bg-white`) instead of the design tokens (`text-foreground`, `bg-paper`, `font-memento-serif`). The sidebar is properly styled but the content area looks like a different app. This story transforms the admin console into a **production-ready management dashboard** with real data, real metrics, and real controls. --- ## User Stories ### US-1: Real Dashboard Metrics **As an** admin, **I want** to see real, live metrics on the dashboard, **So that** I understand the health and growth of the platform. **Acceptance Criteria:** - **Given** I navigate to `/admin` - **When** the page loads - **Then** I see 6 metric cards with real data: | Metric | Source | Computation | |--------|--------|-------------| | Total Users | `User.count()` | Current count | | Total Notes | `Note.count()` | Current count | | AI Requests (30d) | `UsageLog.sum(requestsCount)` where `periodStart` in last 30 days | Aggregate | | Active Subscribers | `Subscription.count()` where `status = ACTIVE` and `tier != BASIC` | Count | | Tokens Used (30d) | `UsageLog.sum(tokensUsed)` where `periodStart` in last 30 days | Aggregate, formatted (e.g., "1.2M") | | Revenue (30d) | Stripe API or `Subscription` count × price | If Stripe not connected, show subscriber count × plan price as estimate | - **And** each card shows a trend arrow comparing to the previous 30-day period (computed from `UsageLog` and `User.createdAt`) - **And** data is fetched server-side (no client loading state for metrics) - **And** the page uses the `font-memento-serif` heading + `text-foreground` / `bg-paper` design tokens (NOT raw gray/white) ### US-2: Dashboard Charts **As an** admin, **I want** to see usage trends over time, **So that** I can spot patterns and growth trajectories. **Acceptance Criteria:** - **Given** I am on the dashboard - **When** I scroll below the metric cards - **Then** I see a 7-day area chart showing daily AI requests (sourced from `UsageLog` grouped by `periodStart` day) - **And** I see a user growth line chart (users created per day, last 30 days) - **And** charts use Recharts (already a dependency — verify) with the Momento color palette: - Line/area: `#ACB995` (sage) or `#D4A373` (ochre) - Grid: `border-border/40` - Labels: `text-[11px] text-muted-foreground` - Background: transparent (inherits `bg-paper`) ### US-3: Recent Activity Feed **As an** admin, **I want** to see recent platform activity, **So that** I can monitor what's happening in real-time. **Acceptance Criteria:** - **Given** I am on the dashboard - **When** I view the "Recent Activity" section - **Then** I see the last 20 significant events: - New user registration (icon: `UserPlus`, text: "[Name] signed up", relative time) - Subscription created/upgraded (icon: `Crown`, text: "[Name] upgraded to [Tier]", relative time) - Subscription canceled (icon: `AlertTriangle`, text: "[Name] canceled [Tier]", relative time) - High usage alert (icon: `Zap`, text: "[Name] used 90%+ of quota", relative time) - **And** each event shows relative time ("2 minutes ago", "3 hours ago") - **And** events are sourced from `UsageLog` (high usage), `Subscription.createdAt` (new subs), `Subscription.canceledAt` (cancellations), `User.createdAt` (signups) ### US-4: Enhanced User Management **As an** admin, **I want** to see and manage comprehensive user data, **So that** I can support users and manage the platform. **Acceptance Criteria:** - **Given** I navigate to `/admin/users` - **When** the user table loads - **Then** each row shows: - Name + avatar initial - Email - Role badge (USER / ADMIN) - **Subscription tier** badge (Free / Pro / Business / Enterprise) with color: - Free: `bg-secondary text-secondary-foreground` - Pro: `bg-violet-100 text-violet-700` - Business: `bg-amber-100 text-amber-700` - Enterprise: `bg-emerald-100 text-emerald-700` - **AI usage this period** (sum of `requestsCount` from current period `UsageLog`, formatted as number) - Created date - Actions (role toggle, delete, **new: view details**) - **And** there is a **search bar** at the top that filters by name or email (client-side for now, debounced 300ms) - **And** there is **pagination** (25 users per page) with page controls - **And** there is a **subscription filter** dropdown: All / Free / Pro / Business / Enterprise - **And** clicking a user row opens a **User Detail Drawer** (slide-in from right, 400px) ### US-5: User Detail Drawer **As an** admin, **I want** to see detailed information about a specific user, **So that** I can troubleshoot issues and manage their account. **Acceptance Criteria:** - **Given** I click on a user row in the user table - **When** the drawer opens - **Then** I see: **Profile Section:** - Avatar initial + name + email - Role badge - Member since date - Last active (from most recent `UsageLog.createdAt` or `session.expires`) **Subscription Section:** - Current plan badge (tier) - Status badge (ACTIVE / CANCELED / PAST_DUE / TRIALING) - Billing period: "[start] – [end]" - Stripe customer ID (clickable link to Stripe dashboard if available) - "Manage in Stripe" button (opens portal for that customer) **Usage Section:** - Per-feature usage table for current period: - Feature name - Requests used / limit - Tokens used - Progress bar (same visual as UsageBreakdown from Story 3.7) - "View full history" link (future scope, just a placeholder for now) **Account Actions:** - Edit name (inline edit, save via server action) - Reset password (sends reset email) - Change role dropdown - Impersonate user (future scope — button disabled with tooltip "Coming soon") - Delete user (red, with double-confirm for users with notes) - **And** the drawer uses the same design tokens as the settings panels (`rounded-xl border border-border/40 bg-paper`) - **And** closing the drawer refreshes the user list ### US-6: Subscriptions Overview Page **As an** admin, **I want** to see all subscriptions in one place, **So that** I can monitor revenue and plan distribution. **Acceptance Criteria:** - **Given** I navigate to `/admin/subscriptions` (new sidebar nav item) - **When** the page loads - **Then** I see: **Summary Cards (top):** - Total subscribers (count of non-BASIC active subscriptions) - Monthly recurring revenue estimate (subscriber count × plan price) - Churn rate (canceled in last 30d / total subscribers 30d ago) - Most popular plan (tier with highest count) **Subscriber Table:** - Columns: User name, Email, Tier, Status, Period end, MRR, Actions - Filterable by tier and status - Sortable by period end (default: expiring soonest first) - Paginated (25 per page) - Actions: View user drawer, Open Stripe portal, Cancel subscription (with confirm) **Tier Distribution:** - Simple horizontal bar chart showing count per tier - Colors: Free (gray), Pro (violet), Business (amber), Enterprise (emerald) - **And** the sidebar nav includes "Subscriptions" with a `CreditCard` icon between "Users" and "AI Management" ### US-7: Usage Analytics Page **As an** admin, **I want** to see platform-wide AI usage analytics, **So that** I can optimize costs and understand feature adoption. **Acceptance Criteria:** - **Given** I navigate to `/admin/usage` (new sidebar nav item) - **When** the page loads - **Then** I see: **Period Selector:** Last 7 days / 30 days / 90 days (pill toggle, default 30d) **Aggregate Cards:** - Total AI requests (period) - Total tokens consumed (period, formatted: "1.2M") - Average requests per user (period) - Cost estimate (tokens × $0.002/1K tokens for gpt-4o-mini as rough estimate) **Feature Breakdown:** - Bar chart: requests per feature (semantic_search, auto_tag, auto_title, reformulate, chat, brainstorm_*) - Sorted by volume descending - Each bar shows count + percentage of total **Top Users Table:** - Columns: Rank, User name, Requests, Tokens, Tier - Top 25 users by request count - Clickable rows → user detail drawer **Daily Trend Chart:** - Area chart: daily request count over selected period - Tooltip shows date + count - **And** all data is sourced from `UsageLog` aggregated via Prisma `groupBy` ### US-8: Feature Flags Management **As an** admin, **I want** to toggle features on/off per tier, **So that** I can roll out features gradually. **Acceptance Criteria:** - **Given** I navigate to `/admin/settings` (existing page) - **When** I scroll to a new "Feature Flags" section - **Then** I see a table of feature flags from the `FeatureFlag` model: - Key (string, editable) - Enabled toggle (switch) - Tiers multiselect (checkboxes: BASIC, PRO, BUSINESS, ENTERPRISE) - Last updated date - **And** I can create a new feature flag via "Add Flag" button - **And** I can delete a feature flag - **And** changes take effect immediately (writes to DB, `revalidatePath('/admin/settings')`) - **And** the section uses the existing `SettingsSection` / `SettingToggle` component patterns --- ## Technical Design ### New Files to Create | File | Description | |------|-------------| | `app/(admin)/admin/subscriptions/page.tsx` | Subscriptions overview page | | `app/(admin)/admin/subscriptions/subscription-table.tsx` | Subscriber table client component | | `app/(admin)/admin/usage/page.tsx` | Usage analytics page | | `app/(admin)/admin/usage/usage-client.tsx` | Usage analytics client component (charts) | | `components/admin/user-detail-drawer.tsx` | Slide-in user detail panel | | `components/admin/charts/area-chart.tsx` | Recharts wrapper with Momento theme | | `components/admin/charts/bar-chart.tsx` | Recharts wrapper with Momento theme | | `app/actions/admin-subscriptions.ts` | Server actions: getSubscriptions, cancelSubscription, getSubscriptionSummary | | `app/actions/admin-usage.ts` | Server actions: getUsageAggregate, getUsageByFeature, getTopUsers, getDailyUsage | | `app/actions/admin-feature-flags.ts` | Server actions: getFeatureFlags, createFeatureFlag, updateFeatureFlag, deleteFeatureFlag | ### Files to Modify | File | Change | |------|--------| | `app/(admin)/admin/page.tsx` | Replace mock metrics with real DB queries, add chart components, add activity feed, apply design tokens | | `app/(admin)/admin/users/page.tsx` | Add search bar, subscription filter, apply design tokens, pass subscription data to UserList | | `app/(admin)/admin/user-list.tsx` | Add subscription tier column, usage column, row click handler → drawer, pagination, apply design tokens | | `components/admin-sidebar.tsx` | Add "Subscriptions" and "Usage" nav items | | `components/admin-metrics.tsx` | Apply design tokens (`bg-paper` instead of `bg-white`, `text-foreground` instead of `text-gray-900`) | | `app/(admin)/admin/settings/page.tsx` | Add Feature Flags section | | `app/(admin)/admin/settings/admin-settings-form.tsx` | Add Feature Flags management UI | | `app/actions/admin.ts` | Update `getUsers()` to include subscription and usage data, add pagination params | ### Data Aggregation Queries ```typescript // Dashboard metrics const [ totalUsers, totalNotes, aiRequests30d, // UsageLog.sum({ requestsCount }, { periodStart: { gte: 30dAgo } }) activeSubscribers, // Subscription.count({ status: ACTIVE, tier: { not: BASIC } }) tokensUsed30d, // UsageLog.sum({ tokensUsed }, { periodStart: { gte: 30dAgo } }) prevMonthUsers, // User.count({ createdAt: { lt: 30dAgo } }) prevMonthRequests, // UsageLog.sum for previous period ] = await Promise.all([...]) ``` ```typescript // Daily usage for charts const dailyUsage = await prisma.usageLog.groupBy({ by: ['periodStart'], _sum: { requestsCount: true, tokensUsed: true }, where: { periodStart: { gte: startDate } }, orderBy: { periodStart: 'asc' }, }) ``` ```typescript // Top users const topUsers = await prisma.usageLog.groupBy({ by: ['userId'], _sum: { requestsCount: true, tokensUsed: true }, where: { periodStart: { gte: startDate } }, orderBy: { _sum: { requestsCount: 'desc' } }, take: 25, }) ``` ```typescript // Users with subscription + usage (enhanced getUsers) const users = await prisma.user.findMany({ skip: (page - 1) * 25, take: 25, include: { subscription: { select: { tier: true, status: true, currentPeriodEnd: true } }, usageLogs: { where: { periodStart: { gte: currentPeriodStart } }, select: { requestsCount: true, tokensUsed: true }, }, }, orderBy: { createdAt: 'desc' }, }) ``` ### Dependencies to Verify | Package | Status | Action | |---------|--------|--------| | `recharts` | Verify in package.json | Install if missing (`npm i recharts`) | | `date-fns` | Already installed | No action | | `@tanstack/react-query` | Already installed | No action | ### Design Tokens (Existing — Consistent with Main App) All admin pages MUST use these tokens instead of raw Tailwind colors: | Element | Current (WRONG) | Correct Token | |---------|-----------------|---------------| | Page background | `bg-white` / `bg-gray-50` | `bg-paper` (`#F2F0E9`) | | Text primary | `text-gray-900` | `text-foreground` / `text-ink` | | Text secondary | `text-gray-600` | `text-muted-foreground` / `text-muted-ink` | | Cards | `bg-white border-gray-200` | `bg-paper border-border/40` (or `bg-card`) | | Headings | `text-3xl font-bold text-gray-900` | `font-memento-serif text-3xl font-medium text-foreground` | | Subtitles | `text-gray-600 mt-1` | `text-[11px] text-muted-foreground uppercase tracking-[0.2em]` | | Dark mode | Manual `dark:` classes | Automatic via CSS variables | | Sidebar active | Custom classes | `memento-active-nav` class (already defined) | ### Sidebar Navigation (Updated) ``` ADMIN_NAV_ITEMS = [ { titleKey: 'admin.sidebar.dashboard', href: '/admin', icon: LayoutDashboard }, { titleKey: 'admin.sidebar.users', href: '/admin/users', icon: Users }, { titleKey: 'admin.sidebar.subscriptions', href: '/admin/subscriptions', icon: CreditCard }, { titleKey: 'admin.sidebar.usageAnalytics', href: '/admin/usage', icon: BarChart3 }, { titleKey: 'admin.sidebar.aiManagement', href: '/admin/ai', icon: Brain }, { titleKey: 'admin.sidebar.settings', href: '/admin/settings', icon: Settings }, ] ``` ### i18n Keys to Add (15 locales) All new keys under `admin.*` namespace. The full `admin` block (260+ keys) needs to be propagated to all 15 locale files. New keys for this story: ```json { "admin": { "sidebar.subscriptions": "Subscriptions", "sidebar.usageAnalytics": "Usage Analytics", "dashboard.totalNotes": "Total Notes", "dashboard.aiRequests": "AI Requests", "dashboard.activeSubscribers": "Active Subscribers", "dashboard.tokensUsed": "Tokens Used", "dashboard.revenueEstimate": "Revenue (est.)", "dashboard.recentActivity": "Recent Activity", "dashboard.noActivity": "No recent activity.", "dashboard.newSignup": "{name} signed up", "dashboard.upgraded": "{name} upgraded to {tier}", "dashboard.canceled": "{name} canceled {tier}", "dashboard.highUsage": "{name} reached {percent}% of quota", "dashboard.userGrowth": "User Growth", "dashboard.dailyRequests": "Daily AI Requests", "subscriptions.title": "Subscriptions", "subscriptions.description": "Monitor and manage subscription plans", "subscriptions.totalSubscribers": "Total Subscribers", "subscriptions.monthlyRevenue": "Monthly Revenue (est.)", "subscriptions.churnRate": "Churn Rate", "subscriptions.popularPlan": "Most Popular", "subscriptions.table.user": "User", "subscriptions.table.tier": "Plan", "subscriptions.table.status": "Status", "subscriptions.table.periodEnd": "Period End", "subscriptions.table.mrr": "MRR", "subscriptions.table.actions": "Actions", "subscriptions.manageStripe": "Manage in Stripe", "subscriptions.cancelConfirm": "Cancel subscription for {name}?", "subscriptions.tierDistribution": "Plan Distribution", "usage.title": "Usage Analytics", "usage.description": "Platform-wide AI feature consumption", "usage.totalRequests": "Total Requests", "usage.totalTokens": "Total Tokens", "usage.avgPerUser": "Avg per User", "usage.costEstimate": "Est. Cost", "usage.featureBreakdown": "Feature Breakdown", "usage.topUsers": "Top Users", "usage.rank": "#", "usage.requests": "Requests", "usage.tokens": "Tokens", "usage.dailyTrend": "Daily Trend", "usage.last7d": "Last 7 days", "usage.last30d": "Last 30 days", "usage.last90d": "Last 90 days", "users.search": "Search users...", "users.filterTier": "Filter by plan", "users.allTiers": "All plans", "users.usageThisPeriod": "AI usage", "users.detail.title": "User Details", "users.detail.memberSince": "Member since", "users.detail.lastActive": "Last active", "users.detail.subscription": "Subscription", "users.detail.billingPeriod": "Billing period", "users.detail.stripeId": "Stripe ID", "users.detail.manageStripe": "Manage in Stripe", "users.detail.usagePeriod": "Usage this period", "users.detail.editName": "Edit name", "users.detail.resetPassword": "Send reset email", "users.detail.impersonate": "Impersonate", "users.detail.impersonateSoon": "Coming soon", "users.detail.deleteWithNotes": "This user has {count} notes. Confirm deletion?", "settings.featureFlags": "Feature Flags", "settings.featureFlagsDescription": "Toggle features on/off per subscription tier", "settings.addFlag": "Add Flag", "settings.flagKey": "Key", "settings.flagEnabled": "Enabled", "settings.flagTiers": "Available for", "settings.deleteFlag": "Delete flag", "settings.confirmDeleteFlag": "Delete feature flag \"{key}\"?" } } ``` --- ## Pages Architecture ### `/admin` — Dashboard (Rewrite) ``` ┌─────────────────────────────────────────────┐ │ Dashboard │ ← font-memento-serif │ Overview of your platform │ ← text-muted-foreground ├─────────┬─────────┬─────────┬──────────────┤ │ Users │ Notes │ AI Req │ Subscribers │ ← 4 real metrics │ 142 ↑12│ 3,241 ↑8│ 12.4K ↑24│ 23 ↑15% │ ├─────────┴─────────┴─────────┴──────────────┤ │ ┌───────────────────┐ ┌───────────────────┐│ │ │ Daily AI Requests │ │ User Growth ││ ← Recharts │ │ ▁▂▃▅▇█▇▅▃▂▁▁▂▃ │ │ ▁▂▃▃▄▅▅▆▇▇█ ││ │ └───────────────────┘ └───────────────────┘│ ├─────────────────────────────────────────────┤ │ Recent Activity │ │ ● Sepehr upgraded to Pro — 2 min ago │ │ ● Jane signed up — 15 min ago │ │ ● ... │ └─────────────────────────────────────────────┘ ``` ### `/admin/users` — Enhanced User Management (Rewrite) ``` ┌─────────────────────────────────────────────┐ │ Users [+ Add] │ │ Manage users and permissions │ ├─────────────────────────────────────────────┤ │ 🔍 Search... [Plan ▼] [All plans] │ ├──────┬────────┬──────┬──────┬──────┬───────┤ │ Name │ Email │ Role │ Plan │ Usage│ Date │ │ John │ j@e.co │ USER │ Pro │ 847 │ May 2 │ ← row click → drawer │ Jane │ j@e.co │ ADMIN│ Free │ 23 │ Apr 28│ ├──────┴────────┴──────┴──────┴──────┴───────┤ │ ← 1 2 3 → 25 per page│ └─────────────────────────────────────────────┘ ``` ### `/admin/subscriptions` — New Page ``` ┌─────────────────────────────────────────────┐ │ Subscriptions │ │ Monitor and manage subscription plans │ ├─────────┬─────────┬─────────┬──────────────┤ │ 23 Subs │ €228/mo │ 4.2% Churn│ Pro (most) │ ← summary cards ├─────────┴─────────┴─────────┴──────────────┤ │ Tier Distribution │ │ Free ████████████████ 119 │ │ Pro █████████ 15 │ │ Biz ██████ 6 │ ← horizontal bars │ Ent ███ 2 │ ├─────────────────────────────────────────────┤ │ [Tier ▼] [Status ▼] │ ├──────┬──────┬──────┬──────┬────────────────┤ │ User │ Tier │Status│ Until│ Actions │ │ ... │ ... │ ... │ ... │ [Stripe] [Cancel]│ └──────┴──────┴──────┴──────┴────────────────┘ ``` ### `/admin/usage` — New Page ``` ┌─────────────────────────────────────────────┐ │ Usage Analytics │ │ Platform-wide AI feature consumption │ ├─────────────────────────────────────────────┤ │ [7d] [30d] [90d] │ ← period toggle ├──────────┬──────────┬──────────┬────────────┤ │ 12.4K │ 1.2M │ 87 │ $2.40 │ ← aggregate cards │ Requests │ Tokens │ Avg/user │ Est. cost │ ├──────────┴──────────┴──────────┴────────────┤ │ Feature Breakdown │ │ semantic_search ████████████████ 4,200 │ │ auto_tag ██████████ 2,800 │ ← horizontal bars │ auto_title █████ 1,400 │ │ chat ███ 900 │ ├─────────────────────────────────────────────┤ │ Daily Trend │ │ ▁▂▃▅▇█▇▅▃▂▁▁▂▃ │ ← area chart ├─────────────────────────────────────────────┤ │ Top 25 Users │ │ # │ User │ Requests │ Tokens │ Tier │ │ 1 │ Sepehr │ 3,200 │ 420K │ Pro │ │ 2 │ ... │ ... │ ... │ ... │ └─────────────────────────────────────────────┘ ``` --- ## Implementation Order | Step | Task | Est. | |------|------|------| | 1 | Fix design tokens on all existing admin pages (replace `bg-white`/`text-gray-900` with tokens) | 1.5h | | 2 | Rewrite dashboard with real metrics (6 cards) | 2h | | 3 | Add dashboard charts (daily requests + user growth) | 2h | | 4 | Add recent activity feed | 1.5h | | 5 | Enhance `getUsers()` with subscription + usage data + pagination | 1h | | 6 | Add search bar + tier filter + subscription column + pagination to users page | 2h | | 7 | Build user detail drawer | 3h | | 8 | Create subscriptions page (summary + table + tier distribution) | 3h | | 9 | Create usage analytics page (aggregates + charts + top users) | 3h | | 10 | Add feature flags section to settings page | 1.5h | | 11 | Update admin sidebar with 2 new nav items | 0.5h | | 12 | i18n: propagate full `admin.*` block to all 15 locales | 2h | | **Total** | | **~23h** | --- ## Out of Scope - **Impersonate user** — requires careful security design, deferred - **Admin API key management** — viewing/rotating BYOK keys, deferred - **Export admin data as CSV** — deferred to a follow-up - **Real-time WebSocket updates** — polling/refresh is sufficient - **Email notifications for admin events** — deferred - **Admin audit log** (who did what) — deferred to Epic 4 (FR21) - **Backup/restore UI** — the `dump-db.sh` script is sufficient for now - **Advanced charting** (heatmaps, cohort analysis) — deferred - **Bulk user operations** (mass delete, mass role change) — deferred - **SSO/SAML management** — Epic 4 scope --- ## Edge Cases & Error Handling - **No UsageLog data yet** (fresh install) → charts show empty state, metrics show 0 - **Redis down** → usage section shows "Unable to load" gracefully (fail-open) - **Stripe not configured** → subscription page shows "Connect Stripe" CTA instead of subscriber table - **User has no subscription** → show "Free" badge, no subscription section in drawer - **User deletion with notes** → double-confirm dialog showing note count - **Self-management protection** → admin cannot delete self, change own role, or cancel own subscription via admin UI - **Pagination with filters** → filter + search are client-side on the fetched page (acceptable for <10K users); server-side filtering if user base grows - **Dark mode** → all new components must work in both modes via CSS variables --- ## Testing Notes - **Visual**: Verify all pages in both light and dark mode - **Responsive**: Dashboard charts should stack on mobile; tables should scroll horizontally - **i18n**: Switch language and verify all admin keys render correctly - **Data**: Seed test data with `UsageLog` entries across multiple users and periods - **Access**: Verify non-admin users are blocked at middleware level - **Performance**: Dashboard with 10K+ `UsageLog` entries should load in <2s (verify query performance)