Workspace/team/project selects are populated from API on mount.
Falls back to source_apps when projects collection is empty.
Table now shows resolved name + truncated ID hint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fetches available source_apps via breakdown endpoint on mount,
replaces text input with select. Defaults to "All apps".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
project_id is never populated (all null); source_app is the de-facto
project identifier. By Project preset now shows real data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each selected row dimension (user, team, source_app, etc.) now gets
its own <th>/<td> instead of being joined into one combined cell.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Quick view presets: By User, By Model, By Project, By Team
- Resolve user_external_id → full_name (email) from /admin/users
- Resolve project_id → project name from /admin/projects
- Smart cost formatting: $X.XX / $0.XXXX / $0.XXXXXX / exponential
(fixes 0.0000 display for small google_tts values)
- Sortable columns (click any column header)
- Grand total footer row
- Pill-style dimension chip buttons (no hidden checkboxes)
- Source app filter in controls
- Empty / hint states
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add google_tts/google_tts entry to models.yaml (16 USD/1M chars, WaveNet tier)
- Add scripts/migrate_video_accessibility.py for historical data backfill:
migrates 25 users and 103 jobs (Gemini + TTS usage records) from
accessible_video MongoDB into cost tracker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- compute_total_cost: read token_input/token_output/char (new keys)
with fallback to old input_tokens/output_tokens/chars for compat
- _PROVIDER_ALIAS: google/gemini → vertex_ai-language-models
- _infer_provider: gemini → vertex_ai-language-models
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- analytics_service now queries usage_events (not usage_rollups)
so data appears immediately without waiting for nightly rollup
- match on ts datetime range (was string 'date' field — broken)
- correct units field names: token_input/token_output/char
- rollup_daily.py: same field name fix for historical aggregation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.css: full CSS variables design system (tokens, reset, typography)
Dark sidebar (#0f172a) + light content (#f8fafc) + indigo accent (#6366f1)
- App.tsx: sidebar with inline SVG icons, two-section nav (Analytics / Management),
CSS class-based layout replacing all inline styles
- Login.tsx: branded login card on dark radial gradient, proper Microsoft SSO button
with brand colors, dev login section with dashed divider and amber badge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- routes_auth: POST /v1/auth/dev-login — bypass Azure AD (disabled in production),
creates admin in DB and sets JWT cookie; takes email + full_name
- routes_auth: use settings.frontend_callback_url instead of parsing CORS origins
for SSO post-login redirect — configurable per environment
- config: add frontend_callback_url setting
- dependencies: fix get_current_admin — was querying _id as string (ObjectId bug)
and filtering is_active:True (never set by SSO flow)
Frontend:
- Login.tsx: dev login form shown in non-production builds below SSO button
- api.ts: use import.meta.env.BASE_URL so API paths work under any subpath prefix
- main.tsx: pass BASE_URL as BrowserRouter basename for correct SPA routing
- vite.config.ts: read VITE_BASE_PATH env var to set Vite base (default /)
- nginx.conf: serve app at /cost-tracker/ prefix, proxy API routes internally
- Dockerfile: accept VITE_BASE_PATH build arg, copy build to /cost-tracker/ subdir
Infra:
- docker-compose.yml: API host port 8003 (8001 taken by ppt-tool on optical-dev)
- infra/deploy/apache-cost-tracker.conf: Apache include for optical-dev routing
- infra/deploy/deploy.sh: one-shot deploy script (clone/pull, build, Apache config)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- database.py: add connect_db/disconnect_db aliases, get_db_instance() sync
getter, get_db() async dependency, and optional db arg on create_indexes()
to match all call sites in main.py, celery_worker.py, and route modules
- docker-compose.yml: remove host-port exposure for mongodb/redis to avoid
conflicts with existing services on optical-dev (:27017, :6379 already in use)
- .env.example: use Docker service-name hostnames (mongodb/redis) instead of
localhost so the API container can reach them inside the Compose network
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>