learning_ai_common_plat/dashboards/admin-web/README.md
saravanakumardb1 2d54795c30 feat(dashboards): migrate admin + tracker dashboards to common-plat as product-agnostic
- Copy admin-dashboard-web → dashboards/admin-web
- Copy tracker-dashboard-web → dashboards/tracker-web
- Update pnpm-workspace.yaml to include dashboards/*
- Replace file: refs with workspace:* for @bytelyst/* packages
- Replace all hardcoded LysnrAI/lysnn.com branding with generic platform refs
- Make telemetry use NEXT_PUBLIC_PRODUCT_ID / PRODUCT_ID env vars
- Update mock credentials, seed data, invitation codes, placeholders
- Update READMEs, e2e tests, unit tests for product-agnostic content
- Both dashboards pass tsc --noEmit clean
2026-02-28 02:17:35 -08:00

207 lines
9.7 KiB
Markdown

# Platform Admin Dashboard
Product-agnostic admin console for the ByteLyst platform. Connects **directly** to Azure Cosmos DB via the `@azure/cosmos` SDK — no intermediate API server. Set `PRODUCT_ID` in `.env.local` to deploy for any product (e.g. `lysnrai`, `chronomind`, `nomgap`, `mindlyst`).
---
## Tech Stack
| Layer | Technology |
| ------------- | ------------------------------------------------ |
| **Framework** | Next.js 16 (App Router, React 19) |
| **Styling** | TailwindCSS v4 + shadcn/ui (New York style) |
| **Database** | Azure Cosmos DB (NoSQL, Serverless) — direct SDK |
| **Auth** | JWT (jose) + bcrypt (bcryptjs), server-side only |
| **Charts** | Recharts |
| **Icons** | Lucide React |
| **Language** | TypeScript 5 |
## Getting Started
```bash
npm install
cp .env.local.example .env.local # Fill in real Azure credentials
npm run dev # http://localhost:3001
npm run check # TypeScript + ESLint
npm run build # Production build
```
### Seed the Database
Creates all Cosmos containers + default admin/viewer users:
```bash
curl -X POST "http://localhost:3001/api/seed?secret=<SEED_SECRET>"
```
### Default Logins
| Email | Password | Role |
| ------------------ | ----------- | ----------- |
| `admin@example.com` | `Admin123!` | Super Admin |
| `viewer@example.com` | `viewer123` | Viewer |
## Environment Variables
Copy `.env.local.example``.env.local`:
| Variable | Required | Description |
| ------------------------- | -------- | ---------------------------------------- |
| `COSMOS_ENDPOINT` | Yes | Cosmos DB endpoint URL |
| `COSMOS_KEY` | Yes | Cosmos DB primary key |
| `COSMOS_DATABASE` | No | Database name (set per product) |
| `COSMOS_REGION` | No | Region (default: `westus2`) |
| `JWT_SECRET` | Yes | Shared JWT signing secret |
| `SEED_SECRET` | Yes | Secret for POST /api/seed |
| `AZURE_KEYVAULT_URL` | No | Azure Key Vault URL |
| `AZURE_SPEECH_KEY` | No | Speech service key (for future features) |
| `AZURE_SPEECH_REGION` | No | Speech region |
| `AZURE_OPENAI_ENDPOINT` | No | OpenAI endpoint |
| `AZURE_OPENAI_KEY` | No | OpenAI key |
| `AZURE_OPENAI_DEPLOYMENT` | No | Model deployment name |
## Pages
| Route | Description |
| ---------------- | ---------------------------------------------------------------- |
| `/` | Dashboard overview — KPIs, charts, recent activity |
| `/users` | User management — search, filter, detail dialogs |
| `/subscriptions` | Plan management — pricing, features, create plans |
| `/tokens` | API token management — create, revoke, scopes |
| `/usage` | Usage analytics — token/request charts, model costs |
| `/audit` | Audit log — admin actions, security events |
| `/settings` | Platform config — kill switch, Azure, rate limits, notifications |
| `/login` | Authentication page |
## API Routes
All routes are in `src/app/api/`:
| Endpoint | Method | Description |
| --------------------------- | -------------- | ------------------------------------------ |
| `/api/auth/login` | POST | Authenticate user (email + password → JWT) |
| `/api/auth/me` | GET | Get current user from Bearer token |
| `/api/users` | GET/POST | List/create users |
| `/api/users/[id]` | GET/PUT/DELETE | User CRUD by ID |
| `/api/tokens` | GET/POST | List/create API tokens |
| `/api/usage` | GET | Usage analytics |
| `/api/audit` | GET | Audit log entries |
| `/api/dashboard/stats` | GET | Dashboard KPI statistics |
| `/api/settings/kill-switch` | GET/PUT | Platform kill switch (read/toggle) |
| `/api/seed` | POST | Initialize containers + seed default users |
## Architecture
```
src/
├── app/
│ ├── (dashboard)/ # Protected routes (sidebar + auth guard)
│ │ ├── layout.tsx # Dashboard shell (sidebar, error boundary)
│ │ ├── loading.tsx # Skeleton loading state
│ │ ├── page.tsx # Dashboard overview
│ │ ├── users/page.tsx
│ │ ├── subscriptions/page.tsx
│ │ ├── tokens/page.tsx
│ │ ├── usage/page.tsx
│ │ ├── audit/page.tsx
│ │ └── settings/page.tsx # Includes kill switch toggle
│ ├── api/ # Next.js API routes → direct Cosmos DB
│ │ ├── auth/login/route.ts
│ │ ├── auth/me/route.ts
│ │ ├── users/route.ts
│ │ ├── users/[id]/route.ts
│ │ ├── tokens/route.ts
│ │ ├── usage/route.ts
│ │ ├── audit/route.ts
│ │ ├── dashboard/stats/route.ts
│ │ ├── settings/kill-switch/route.ts
│ │ └── seed/route.ts
│ ├── login/page.tsx # Public login page
│ ├── layout.tsx # Root layout (providers)
│ └── providers.tsx # Auth + Theme providers
├── components/
│ ├── ui/ # 16 shadcn/ui primitives (avatar, badge, button, card, etc.)
│ ├── sidebar-nav.tsx # Responsive sidebar (mobile hamburger)
│ ├── auth-guard.tsx # Route protection → redirect to /login
│ └── error-boundary.tsx # Catch page crashes gracefully
└── lib/
├── cosmos.ts # Cosmos DB client singleton + container registry
├── auth-server.ts # JWT create/verify + bcrypt (server-side only)
├── auth-context.tsx # Client auth provider (localStorage tokens)
├── theme-context.tsx # Dark/light/system mode
├── api.ts # Client-side API helper functions
├── mock-data.ts # Mock data + types (fallback when Cosmos unavailable)
├── utils.ts # cn() tailwind merge helper
└── repositories/ # Direct Cosmos DB CRUD
├── users.ts # getUserById, getUserByEmail, createUser, updateUser
├── tokens.ts # API token CRUD
├── audit.ts # Audit log read/write
└── usage.ts # Usage statistics
```
## Cosmos DB Integration
The dashboard connects **directly** to Cosmos DB — no intermediate backend server.
**Pattern:**
```
Page Component → fetch("/api/...") → API Route → Repository → Cosmos SDK → Azure
```
**Key files:**
- `lib/cosmos.ts` — singleton `CosmosClient`, `getContainer(name)`, `initializeAllContainers()`
- `lib/repositories/*.ts` — CRUD functions per container
- `lib/auth-server.ts``authenticateUser()`, `createAccessToken()`, `verifyToken()`
**Container definitions** (in `cosmos.ts`):
| Container | Partition Key | TTL |
| ------------- | ------------- | ------- |
| `users` | `/id` | — |
| `licenses` | `/userId` | — |
| `transcripts` | `/userId` | — |
| `usage_daily` | `/userId` | 1 year |
| `settings` | `/userId` | — |
| `audit_log` | `/category` | 90 days |
| `api_tokens` | `/userId` | — |
| `devices` | `/userId` | — |
## Kill Switch
The Settings page has a prominent kill switch card at the top:
- **Green** = platform active, **Red** = platform disabled
- Toggle writes to `settings` container: `{ id: "kill_switch", userId: "system", enabled: bool }`
- All apps (desktop, mobile, web) read this document to check platform status
## Features
- **Direct Cosmos DB** — no middleware, API routes call SDK directly
- **JWT auth** — bcrypt password hashing, HS256 JWT tokens
- **Auth guard** — redirects to `/login` if not authenticated
- **Mock fallback** — pages fall back to mock data if Cosmos is unavailable
- **Responsive** — sidebar collapses to hamburger on mobile
- **Dark mode** — toggle in sidebar footer, persisted to localStorage
- **Error boundary** — catches page crashes gracefully
- **Kill switch** — global platform toggle on Settings page
## Docker
```bash
docker build -t platform-admin .
docker run -p 3001:3000 --env-file .env.local platform-admin
# Or with docker-compose
docker compose up -d
```
## Development Notes
- **Port**: runs on 3001 in dev (3000 may conflict with Docker)
- **shadcn/ui**: components are in `src/components/ui/`, added via `npx shadcn@latest add <component>`
- **Adding a new API route**: create `src/app/api/<name>/route.ts`, import from `@/lib/repositories/*`
- **Adding a new page**: create `src/app/(dashboard)/<name>/page.tsx` — auto-protected by auth guard