livrare lot 2

This commit is contained in:
EVOTECH IT SRL 2026-07-10 03:39:53 -07:00
commit 8ecc78e729
763 changed files with 164593 additions and 0 deletions

View file

@ -0,0 +1,879 @@
# admin-dashboard - Index
Dashboard administrativ pentru platforma DIDI. Aplicatie React SPA (Single Page Application) cu Material UI, montata la `/admin`. Ofera interfata grafica pentru managementul complet al platformei: monitorizare servicii, configurare framework analiza, management utilizatori, configurare modele LLM, vizualizare si executie pipeline-uri, si istoric analize.
**Stack**: React 19 + TypeScript + Material UI 7 + React Router 7
**Container**: didi-admin-local (nginx) -- porturi host `3081` -> 80 (HTTP) si `3001` -> 443 (HTTPS)
**Port intern**: 80/443 (nginx)
**Basename**: /admin (toate rutele prefixate cu /admin)
**Autentificare**: Keycloak (client `admin-dashboard`, PKCE S256, realm `didi-admins`), Keycloak LOCAL `didi-keycloak` servit sub `/auth` prin nginx-ul dashboard-ului
**Build**: node:20-alpine (build) -> nginx:alpine (serve)
**Network**: `didi-network` (single unified network for all DIDI + AI platform stacks since 2026-05-04)
## URL-uri de acces
| URL | Cum funcționează |
|---|---|
| `https://<host-local>:3001/admin/` | direct la containerul `didi-admin-local` (nginx HTTPS, port host 3001) |
| `http://<host-local>:3081/admin/` | direct la containerul `didi-admin-local` (nginx HTTP, port host 3081) |
| `https://didi365.eu/admin/` | public via Cloudflare tunnel + frontend nginx (când Cloudflare permite path) |
| `https://didi365.eu/admin-backend/` | public alias 301 → `/admin/` |
`<host-local>` = mașina de deployment (host-ul local). Containerul `didi-admin-local` are propria nginx (porturi host 3081/3001); frontend `didi-frontend` adaugă proxy `/admin/*`. Detalii: `frontend/INDEX.md` secțiunea 21.
---
## Ce face
1. **Monitorizare servicii** -- health check pentru toate 14 serviciile backend (polling 30s)
2. **Configurare framework analiza** -- CRUD complet pe 20+ tabele de parametri (dimensiuni, tehnici, indicatori, verdicte, ponderi, surse, claims)
3. **Management utilizatori** -- lista, cautare, editare, stergere, sincronizare Keycloak, management planuri abonament
4. **Configurare modele LLM** -- provideri, modele, assignments pe componente/etape, chei API, teste conectivitate
5. **Istoric analize** -- vizualizare si stergere sesiuni analiza din agent-v3 (filtre: risk, status, date)
6. **Pipeline builder** -- editor vizual DAG (ReactFlow) pentru pipeline-uri de analiza
7. **Executie si monitorizare** -- lansare pipeline-uri, tracking progres real-time (WebSocket + polling)
8. **Catalog componente** -- registry pentru functii, resurse AI, transformatoare, modele LLM
9. **Sincronizare Redis** -- trigger manual sync parametri framework -> Redis (pentru agent-v3)
---
## Rute (App.tsx)
Toate rutele sunt protejate cu ProtectedRoute (verifica autentificare Keycloak).
| Path (relativ la /admin) | Componenta | Scop |
|---------------------------|-----------|------|
| / | ServicesDashboard | Landing page: monitorizare health servicii |
| /dashboard | ServicesDashboard | Alias pentru / |
| /login | LoginRedirect | Trigger Keycloak login |
| /orchestrator | OrchestratorDashboard | Pipeline builder + catalog + executie + istoric |
| /orchestrator/runs/:runId | RunDetailsPage | Detalii run individual (timeline, I/O, raw) |
| /pipelines | PipelinesPage | **NEW** — CRUD + lifecycle pipeline-uri reale (`input_type_profile`) + dry-run/cancel (Modul 1); admin-only |
| /analysis | UniversalAnalysisDashboard | Executie analiza multi-modal (text/image/video/audio/URL) |
| /analysis/results/:runId | UniversalResultsPage | Rezultate live cu polling |
| /framework | FrameworkDashboard | CRUD complet parametri framework DIDI |
| /framework/progressive | ProgressiveAnalysisTree | Vizualizare arbore analiza progresiva |
| /users | UserManagement | Management utilizatori + planuri abonament |
| /history | AnalysisHistory | Istoric sesiuni analiza admin |
| /providers | ProvidersManagement | CRUD provideri LLM, modele, assignments, chei API |
| /techniques-v3 | TechniquesV3Config | Configurare modele pe etape techniques |
| /llm-components | LLMComponentsConfig | Configurare unificata LLM (Techniques + AI-Tampered + Claims + Verdict) |
| /text-analysis | UniversalAnalysisDashboard | Legacy (randeaza direct, nu redirect) |
| /image-analysis | UniversalAnalysisDashboard | Legacy (randeaza direct, nu redirect) |
| /video-analysis | UniversalAnalysisDashboard | Legacy (randeaza direct, nu redirect) |
| /audio-analysis | UniversalAnalysisDashboard | Legacy (randeaza direct, nu redirect) |
| /moderation | ModerationQueue | Lista pending HIL review queue cu filtre + paginatie |
| /moderation/:queueId | ModerationDetail | Detalii sesiune + Resolve dialog (action + corrections JSON + notes) |
| /moderation/stats | ModerationStats | Cards count (pending, in_review, resolved_24h) + by-priority + avg time |
| /unauthorized | Unauthorized | 403 page pentru useri fara rol staff |
---
## Structura fisiere
```
admin-dashboard/
Dockerfile -- Multi-stage: node:20-alpine build -> nginx:alpine
Dockerfile.dev -- Dev build
nginx.conf -- Nginx dev (port 80)
nginx-ssl.conf -- Nginx productie (HTTPS 443 + HTTP 80)
package.json -- React 19, MUI 7, ReactFlow, Recharts, Keycloak JS
.env -- REACT_APP_KEYCLOAK_URL, STAGING_MODE=false
src/
App.tsx -- Router principal (toate rutele) + AuthProvider + ThemeProvider
index.tsx -- Entry point (randeaza App)
services/
api.ts -- Client API principal (axios, 100+ endpoint-uri)
analysisService.ts -- Serviciu analiza legacy (submit/status/results)
keycloak.ts -- Configurare Keycloak JS adapter
contexts/
AuthContext.tsx -- Provider autentificare (token refresh 30s, logout pe 401)
ThemeContext.tsx -- Provider tema dark/light (persistat in localStorage)
hooks/
useAnalysisResults.ts -- Rezultate analiza cu auto-refresh
useRunsHistory.ts -- Istoric run-uri paginat cu filtre
usePipelineProgress.ts -- WebSocket progres pipeline real-time
useDeleteDialog.ts -- Dialog confirmare stergere generic
useExpandedItems.ts -- Stare accordion expandat
useTabState.ts -- Stare tab per item
pages/
LandingPage.tsx -- Pagina marketing (nefolosita in rute)
FrameworkDashboard.tsx -- CRUD framework (20+ tabele)
OrchestratorDashboard.tsx -- Pipeline builder + catalog
RunDetailsPage.tsx -- Detalii run (3 tab-uri)
UniversalAnalysisDashboard.tsx -- Executie analiza multi-modal
UniversalResultsPage.tsx -- Rezultate live polling
AudioAnalysisDashboard.tsx -- Legacy
ImageAnalysisDashboard.tsx -- Legacy
TextAnalysisDashboard.tsx -- Legacy
VideoAnalysisDashboard.tsx -- Legacy
CostDashboard/ -- Dashboard costuri LLM (orfan, nerutuit)
components/
auth/
LoginRedirect.tsx -- Redirect la Keycloak login
ProtectedRoute.tsx -- Guard autentificare pe rute (suporta requiredAnyRole?: string[] any-of)
Unauthorized.tsx -- NEW: 403 page cu lista roluri + "Go to Public App" + "Log out"
dashboard/
ServicesDashboard.tsx -- Grid servicii cu health check
services/
ServiceCard.tsx -- Card individual serviciu
framework/
CrudDataTable.tsx -- Tabel CRUD generic (GET/POST/PUT/DELETE)
TechniquesTreeView.tsx -- Arbore tehnici
ProgressiveAnalysisTree.tsx -- Arbore analiza progresiva
AnalysisFlowVisualization/ -- Vizualizare ReactFlow DAG framework
orchestrator/
PipelineBuilder.tsx -- Editor vizual DAG (ReactFlow)
PipelineManager.tsx -- Lista + CRUD pipeline-uri
PipelineList.tsx -- Lista pipeline-uri
PipelineExecutor.tsx -- Executor pipeline cu polling 500ms
ComponentRegistry.tsx -- Catalog componente (functii, resurse, transformatoare)
benchmark/
PipelineBenchmark.tsx -- Executie si monitorizare benchmark
useBenchmarkRunner.ts -- Hook benchmark (execute + poll)
runs-history/
RunsHistoryPage.tsx -- Istoric run-uri paginat
RunsHistoryTable.tsx -- Tabel run-uri
RunDetailsDrawer.tsx -- Drawer detalii run
NodeWaterfallTimeline.tsx -- Timeline executie noduri
nodes/, edges/, dialogs/, hooks/ -- Sub-componente pipeline builder
Pipelines/ -- NEW (Modul 1)
PipelinesPage.tsx -- Tabel pipeline-uri reale (input_type_profile) + lifecycle (clone/activate/versions/restore/import/export)
DryRunDialog.tsx -- Run Console-lite: dry-run pe agent-v3 (flow + noduri + verdict profile, fara executie reala)
pipelinesApi.ts -- Client catre /framework/api/pipelines + /agent-v3/api/v3/pipeline/dry-run|cancel
UserManagement/
UserManagement.tsx -- Tab utilizatori + planuri
UserEditModal.tsx -- Dialog editare utilizator
AnalysisHistory/
AnalysisHistory.tsx -- Tabel istoric sesiuni analiza
AnalysisDetailModal.tsx -- Modal detalii sesiune
ProvidersManagement/
ProvidersManagement.tsx -- CRUD 4 tabele: provideri, modele, assignments, chei
TechniquesV3/
TechniquesV3Config.tsx -- Configurare modele techniques
LLMComponentsConfig/
index.tsx -- Config unificata 3 componente + verdict + provideri
VerdictConfig.tsx -- Configurare verdict (ponderi, categorii, severitate)
Moderation/ -- NEW
api.ts -- fetch helpers + types + UI helpers (priorityColor, statusColor)
ModerationQueue.tsx -- paginated table + filters (status, priority), auto-refresh 30s pe pending tab
ModerationDetail.tsx -- back btn + action buttons (Claim/Approve/Resolve corrected/Reject) + side-by-side cards (Input + AI Verdict) + Resolve dialog cu JSON corrections editor
ModerationStats.tsx -- 3 stat cards + by-priority chips + avg time
ModerationSettings/ -- NEW
index.tsx -- main panel cu 4 cards + auto-sync after save
TriageCard.tsx -- toggle, slider confidence_low, TextFields risk_grey + queue thresholds
BrainClientCard.tsx -- toggle, URL, timeouts, sliders confidence_min_silver + semantic_threshold, per-component checkboxes (techniques/ai_tampered/claims). Include alert "CLIENT settings only -- server config in AI platform"
SensitiveTopicsCard.tsx -- chip list cu delete + add form (code regex validation)
RolesCard.tsx -- table cu toggles (can_resolve, can_escalate, can_force_gold_brain, is_active)
api.ts -- fetch helpers pentru /api/moderation-config, /api/sensitive-topics, /api/moderation-roles + sync-redis trigger
text-analysis/ -- Componente analiza text legacy
image-analysis/ -- Componente analiza imagini legacy
audio-analysis/ -- Componente analiza audio legacy
video-analysis/ -- Componente analiza video legacy
config/
serviceGroups.ts -- Definitie grupuri servicii sidebar
providerConfigs.ts -- Configurare provideri LLM
theme/
index.ts -- Tema MUI (light/dark, culori, tipografie)
types/
catalog.ts -- Tipuri catalog componente
keycloak.d.ts -- Tipuri Keycloak
```
---
## Servicii backend consumate
Aplicatia nu are backend propriu. Toate datele vin prin nginx reverse proxy de la 3 servicii + health checks directe.
### 1. didiFramework (proxiat la /framework)
Nginx: `/framework/` -> `http://didi-framework:3005`
Folosit de: FrameworkDashboard, UserManagement, ProvidersManagement, LLMComponentsConfig, VerdictConfig
### 2. agent-v3 (proxiat la /agent-v3)
Nginx: `/agent-v3/` -> `http://didi-agent-v3:24803`
Folosit de: AnalysisHistory, TechniquesV3Config, LLMComponentsConfig, ServicesDashboard (health)
### 3. Orchestrator API (proxiat la /api/v1 prin Kong)
Nginx: `/api/` -> `http://didi-kong:8000` (Kong LOCAL)
Folosit de: OrchestratorDashboard, UniversalAnalysisDashboard, ComponentRegistry, PipelineManager, RunsHistory, CostDashboard
### 4. Health checks directe
Nginx proxiaza catre containere individuale pentru verificare disponibilitate.
---
## Endpoint-uri API consumate
### Health checks (ServicesDashboard, polling 30s)
| Metoda | Endpoint | Serviciu verificat |
|--------|----------|--------------------|
| GET | /framework/ | didiFramework |
| GET | /agent-v3/api/v3/health | agent-v3 |
| GET | /api/status | Kong |
| GET | /auth/realms/didi-admins | Keycloak (local didi-keycloak) |
| GET | /health-check/redis-commander | Redis Commander |
| GET | /health-check/rabbitmq | RabbitMQ Management |
| GET | /health-check/minio | MinIO |
| GET | /health-check/pgadmin | pgAdmin |
### Framework CRUD (FrameworkDashboard, baza /framework)
Fiecare endpoint suporta GET (lista), POST (creare), PUT /{id} (update), DELETE /{id} (stergere). Implementat prin componenta generica CrudDataTable.
| Categorie | Endpoint-uri |
|-----------|-------------|
| Analysis Core | /api/dimensions, /api/subdimensions |
| Tehnici | /api/techniques, /api/indicators, /api/validation-rules |
| Evaluare Sursa | /api/platforms, /api/platform-modifiers, /api/source-credibility, /api/domain-age-scores, /api/domain-risk-levels, /api/domain-red-flags, /api/author-classifications, /api/author-credibility |
| Claims | /api/claims/status, /api/claims/types, /api/claims/confidence, /api/claims/interpretation |
| Verdicte | /api/verdicts/categories, /api/verdicts/risk, /api/verdicts/severity |
| Ponderi | /api/weights/components, /api/weights/scenarios, /api/weights/multipliers |
| Provideri | /api/providers/configs, /api/providers/models, /api/providers/assignments, /api/providers/keys |
Extra:
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /api/overview/stats | Statistici framework |
| GET | /api/sync-redis/status | Status ultima sincronizare |
| POST | /api/sync-redis | Trigger sincronizare Redis |
### Management utilizatori (UserManagement, baza /framework)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /api/admin/users?page=&limit=&search=&planId= | Lista utilizatori paginata cu filtre |
| GET | /api/admin/plans | Lista planuri abonament |
| DELETE | /api/admin/users/:id | Dezactivare utilizator |
| PUT | /api/admin/users/:id/email-verified | Toggle email verificat |
| POST | /api/admin/users/sync | Sincronizare utilizatori Keycloak -> PostgreSQL |
| PUT | /api/admin/plans/:id | Editare plan (nume, descriere, credite, pret) |
### Istoric analize admin (AnalysisHistory, baza /agent-v3/api/v3)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /pipeline/history/admin?page=&limit=&search=&risk_level=&status=&from_date=&to_date= | Lista sesiuni cu filtre |
| GET | /pipeline/history/admin/:sessionId | Detalii sesiune |
| DELETE | /pipeline/history/admin/:sessionId | Stergere sesiune |
### Configurare modele LLM (TechniquesV3Config + LLMComponentsConfig, baza /agent-v3/api/v3)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /{component}/config | Configurare componenta |
| GET | /{component}/models | Modele disponibile |
| GET | /{component}/stage-assignments | Assignments pe etape |
| PUT | /{component}/stage-assignments | Salvare assignments |
| POST | /{component}/test-model | Test conectivitate model |
| POST | /{component}/analyze | Test analiza (doar techniques) |
| GET | /{component}/prompts | Lista prompturi componenta |
| PUT | /{component}/prompts/:id | Update prompt |
Unde {component} = techniques, ai-tampered, claims.
### Moderation queue (ModerationQueue + Detail + Stats, baza /agent-v3/api/v3/moderation)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /queue?status=&priority=&assigned_to=me&limit=20&offset=0 | Lista paginata cu filtre |
| GET | /queue/:queueId | Detail (queue entry + full session JOIN) |
| POST | /queue/:queueId/claim | Atomic claim by current moderator |
| PUT | /queue/:queueId/resolve | body: {action, corrections?, notes?, user_id?} |
| POST | /flag | User report (browser extension) |
| GET | /stats | Counts + averages |
### Moderation Settings (ModerationSettings, baza /framework/api)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /moderation-config | Single-row config |
| PUT | /moderation-config | Update whitelisted fields |
| GET | /sensitive-topics?active=all | List topics |
| POST | /sensitive-topics | Create topic |
| PUT | /sensitive-topics/:id | Update label/is_active |
| DELETE | /sensitive-topics/:id | Soft delete |
| GET | /moderation-roles | List roles |
| PUT | /moderation-roles/:code | Update permission toggles |
### Provideri LLM (ProvidersManagement, baza /framework/api/providers)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /all | Toti providerii cu modele |
| POST | /configs | Creare provider |
| PUT | /configs/:id | Update provider |
| DELETE | /configs/:id | Stergere provider |
| POST | /models | Creare model |
| PUT | /models/:id | Update model |
| DELETE | /models/:id | Stergere model |
| POST | /assignments | Creare assignment |
| PUT | /assignments/:id | Update assignment |
| DELETE | /assignments/:id | Stergere assignment |
| POST | /keys | Creare cheie API |
| PUT | /keys/:id | Update cheie |
| DELETE | /keys/:id | Stergere cheie |
| POST | /test/:providerId | Test conexiune provider |
| GET | /prompts?component={comp} | Lista prompturi componenta |
| PUT | /prompts/:id | Update prompt |
### Orchestrator - Catalog componente (ComponentRegistry, baza /api/v1/catalog)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /functions/ | Lista feature extractors |
| GET | /resources/ | Lista resurse AI |
| GET | /transformers/ | Lista transformatoare |
| GET | /models/ | Lista modele LLM |
| GET | /models/filters/options | Optiuni filtre modele |
| POST | /{type}/ | Creare componenta |
| PUT | /{type}/:id | Update componenta |
| DELETE | /{type}/:id | Stergere componenta |
| PATCH | /{type}/:id | Toggle enabled/disabled |
### Orchestrator - Pipeline-uri (PipelineManager, baza /api/v1/pipelines)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | / | Lista pipeline-uri |
| GET | /:id | Detalii pipeline |
| POST | / | Creare pipeline |
| PUT | /:id | Update pipeline |
| DELETE | /:id | Stergere pipeline |
| GET | /:id/versions | Lista versiuni |
| POST | /:id/versions | Creare versiune |
| GET | /:id/preview | Preview executie |
| POST | /:id/execute | Executie pipeline |
| POST | /:id/versions/:ver/execute | Executie versiune specifica |
### Orchestrator - Run-uri (RunsHistory + RunDetailsPage, baza /api/v1/runs)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | ?limit=&offset=&status=&date_from= | Lista run-uri cu filtre |
| GET | /:runId | Detalii run |
| GET | /:runId?include_live_status=true | Detalii cu status live |
| GET | /:runId/results?include_intermediate=true | Rezultate complete |
| GET | /status/:runId | Status polling |
| GET | /list?limit= | Lista run-uri (analysisResults) |
| GET | /history/enriched?skip=&limit=&pipeline_slug=&verdict= | Istoric imbogatit cu filtre |
| DELETE | /:runId | Stergere run |
### Orchestrator - Template-uri si API-uri externe
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET/POST/PUT/DELETE | /api/v1/dynamic-templates/templates | CRUD template-uri |
| GET | /api/v1/dynamic-templates/templates/tasks/list | Lista task-uri |
| GET | /api/v1/dynamic-templates/templates/external-apis/list | Lista API-uri externe |
| GET | /api/v1/dynamic-templates/templates/feature-extractors/list | Lista extractori |
| GET/POST/PUT/DELETE | /api/v1/task-templates | CRUD task template-uri |
| POST | /api/v1/task-templates/:id/validate | Validare template |
| POST | /api/v1/task-templates/:id/test | Test template |
| POST | /api/v1/task-templates/:id/execute | Executie template |
| GET | /api/v1/external-apis/providers | Lista provideri externi |
| GET/POST/PUT/DELETE | /api/v1/external-apis/apis | CRUD API-uri externe |
| POST | /api/v1/sync/openapi/discover | Descoperire OpenAPI |
| POST | /api/v1/sync/openapi/import | Import servicii descoperite |
| GET | /api/v1/catalog/litellm-catalogue | Catalog LiteLLM |
### Costuri LLM (CostDashboard -- pagina orfana, nerutata)
| Metoda | Endpoint | Scop |
|--------|----------|------|
| GET | /api/v1/costs/summary?start_date=&end_date=&period= | Sumar costuri |
| GET | /api/v1/costs/by-model?start_date=&end_date= | Costuri per model |
| GET | /api/v1/costs/by-job?start_date=&end_date= | Costuri per job |
| GET | /api/v1/costs/thresholds | Praguri configurate |
| GET | /api/v1/costs/alerts | Alerte active |
| POST | /api/v1/costs/thresholds | Creare prag |
| DELETE | /api/v1/costs/thresholds/:id | Stergere prag |
| PUT | /api/v1/costs/alerts/:id/acknowledge | Confirmare alerta |
### WebSocket (progres real-time)
| Endpoint | Scop |
|----------|------|
| wss://{host}/ws/runs/:runId/progress?include_outputs=true | Progres executie pipeline |
Mesaje WebSocket: connected, node_status (update per nod), run_complete, error.
Reconnect cu exponential backoff: 1s -> 2s -> 4s -> ... -> 30s max.
---
## Pagini -- ce face fiecare
### ServicesDashboard (/, /dashboard)
Grid cu carduri pentru toate serviciile grupate pe layere (Data, Gateway/Auth, Orchestration, Monitoring). Fiecare card face health check la 30s si afiseaza status (verde/rosu). Sidebar cu navigare catre paginile admin (Providers, Users, History -- vizibile doar cu rol admin).
Logica: src/components/dashboard/ServicesDashboard.tsx + src/components/services/ServiceCard.tsx
Configurare servicii: src/config/serviceGroups.ts (14 servicii in 4 grupuri)
### FrameworkDashboard (/framework)
Management CRUD complet pentru parametrii framework-ului de analiza. 7 categorii cu sub-tab-uri, fiecare cu tabel editabil (CrudDataTable). Include vizualizare ReactFlow a fluxului de analiza si buton "Sync to Redis" pentru a trimite parametrii catre Redis (agent-v3 ii citeste de acolo).
Logica: src/pages/FrameworkDashboard.tsx (orchestrare tab-uri)
Tabel generic: src/components/framework/CrudDataTable.tsx (GET/POST/PUT/DELETE pe orice endpoint)
Vizualizare: src/components/framework/AnalysisFlowVisualization/
### OrchestratorDashboard (/orchestrator)
Sidebar cu 2 sectiuni: Component Registry (Extractori, Componente AI, Transformatoare) si Pipelines (Lista, Creare, Executie, Istoric). Fiecare sectiune randeaza componenta corespunzatoare.
Logica: src/pages/OrchestratorDashboard.tsx (layout sidebar)
Pipeline builder: src/components/orchestrator/PipelineBuilder.tsx (editor ReactFlow DAG)
Pipeline manager: src/components/orchestrator/PipelineManager.tsx (lista + CRUD)
Catalog: src/components/orchestrator/ComponentRegistry.tsx (tabele + dialoguri CRUD)
Benchmark: src/components/orchestrator/benchmark/PipelineBenchmark.tsx (executie + monitorizare)
Istoric: src/components/orchestrator/runs-history/RunsHistoryPage.tsx (tabel paginat + filtre)
### RunDetailsPage (/orchestrator/runs/:runId)
Detalii complete pentru un run individual. 3 tab-uri: Timeline (waterfall executie noduri), Input/Output (date intrare/iesire), Raw Data (JSON complet). Auto-refresh la 3s cand run-ul e activ. Foloseste fetch() in loc de axios (workaround pentru extensie browser).
Logica: src/pages/RunDetailsPage.tsx
Timeline: src/components/orchestrator/runs-history/NodeWaterfallTimeline.tsx
### PipelinesPage (/pipelines) -- NEW (Modul 1)
Livrabil Modul 1 caiet ("Workflow Builder: CRUD/versionare pipeline; Run Console"). Opereaza pe **definitiile reale de pipeline** (`input_type_profile` din didiFramework), NU pe `pipelineApi` mort (`/api/v1/pipelines`, fara backend). Tabel cu profilurile + actiuni de lifecycle: dry-run, clone, activate/deactivate, versiuni (istoric + restore), import/export.
- **Dry-run** (`DryRunDialog.tsx`): apeleaza `POST /agent-v3/api/v3/pipeline/dry-run` -> arata flow-ul, nodurile (component/queue/priority/stages/prompt keys), componentele skipped si verdict profile-ul rezolvat, fara a rula analiza reala. **Cancel** disponibil pe run-uri active.
- Backing endpoints: `/framework/api/pipelines/*` (list/get/clone/activate/deactivate/PUT cu versionare/versions/restore/import) + `/agent-v3/api/v3/pipeline/dry-run|:id/cancel`.
Logica: src/components/Pipelines/PipelinesPage.tsx + DryRunDialog.tsx + pipelinesApi.ts
Acces: admin-only (`ProtectedRoute requiredRole="admin"`).
### UniversalAnalysisDashboard (/analysis)
Executie analiza multi-modal: text, imagine, video, audio, URL. Sidebar cu 3 sectiuni: Analysis (formular + executor), Results (run-uri active, polling 10s), History (paginat cu filtre). Suporta upload fisiere in MinIO si tracking progres executie.
Logica: src/pages/UniversalAnalysisDashboard.tsx
Polling executie: 2s (GET /api/v1/runs/:runId?include_live_status=true)
Polling run-uri active: 10s (GET /api/v1/runs?limit=50)
### UniversalResultsPage (/analysis/results/:runId)
Pagina rezultate live. Afiseaza progress bar, status per nod (accordion), rezultate finale JSON. Polling la 2s.
Logica: src/pages/UniversalResultsPage.tsx
### UserManagement (/users)
2 tab-uri: Utilizatori (tabel paginat cu cautare, filtrare plan, editare, stergere, sync Keycloak, toggle email-verified) si Planuri Abonament (editare inline: nume, descriere, credite, pret).
Logica: src/components/UserManagement/UserManagement.tsx
Dialog editare: src/components/UserManagement/UserEditModal.tsx
### AnalysisHistory (/history)
Tabel paginat cu toate sesiunile de analiza din agent-v3. Filtre: cautare text, nivel risc (LOW/MODERATE/HIGH/VERY_HIGH/CRITICAL), status, interval date. Coloane: data, email, input preview, risk score, tehnici, AI %, claims, durata. View deschide AnalysisDetailModal cu toate detaliile sesiunii.
**Cost display per analiza (USD)** — in AnalysisDetailModal, sectiunea "LLM Usage" afiseaza:
- Badge total cost langa titlul accordionului (ex: `$0.0321`)
- Grid cu 5 celule: Calls / Prompt Tokens / Completion Tokens / Total Tokens / **Est. Cost**
- Per Component: fiecare card contine cost chip individual (ex: Techniques: `$0.0054`)
Costul se calculeaza client-side dintr-o combinatie de:
1. `llm_usage.by_component[comp]` — prompt_tokens, completion_tokens, models_used[] din agent-v3
2. Fetch parallel la `/framework/api/providers/models` — care returneaza `input_cost_per_1m` + `output_cost_per_1m` per model
3. Formula: `cost = (prompt_tokens / 1M) * input_cost + (completion_tokens / 1M) * output_cost`
4. Cost-ul e charged pe **primul model** din `models_used[]` (aproximare: daca primary a picat si s-a dus pe fallback, cost-ul ar putea fi putin subestimat pe free chain, supra-estimat pe premium — dar in happy path e 100% corect)
Free tier afiseaza tot `$0.00` (Qwen local nu are cost input/output). Premium arata cost real per analiza — tipic $0.02-0.05 pentru text simplu, pana la $0.10-0.15 pentru media cu claims multiple.
Logica: src/components/AnalysisHistory/AnalysisHistory.tsx
Modal detalii: src/components/AnalysisHistory/AnalysisDetailModal.tsx (helpers: `normalizeModelKey`, `computeTokenCost`, `formatUsd`)
### ProvidersManagement (/providers)
CRUD pe 4 tab-uri: Providers (configurare provideri LLM), Models (modele per provider), Assignments (assignment componenta -> model pe etapa), API Keys (chei de acces). Fiecare tab cu dialoguri add/edit/delete. Buton test conexiune.
Logica: src/components/ProvidersManagement/ProvidersManagement.tsx
### TechniquesV3Config (/techniques-v3)
3 tab-uri: Stage Assignments (model primar + 3 fallback-uri per etapa screening/deep), Available Models (catalog cu info speed/quality/cost), Test Analysis (textarea + buton executie test). Comunicare cu agent-v3.
Logica: src/components/TechniquesV3/TechniquesV3Config.tsx
### LLMComponentsConfig (/llm-components)
Pagina unificata pentru configurare LLM si scoring pe toate componentele. Top-level tab-uri: Techniques, AI Tampered, Claims, Source Assessment, Verdict, **Moderation** (NEW), Providers.
**Moderation** (NEW) — al 6-lea buton (alaturi de Techniques, AI Tampered, Claims, Source Assessment, Verdict). Randeaza `<ModerationSettings />` cu 4 cards pentru HIL triage + brain client + sensitive topics + roles. Auto-syncs to Redis dupa fiecare save (snackbar feedback).
**Toggle FREE | PREMIUM** (Etapa 6) — peste Stage Assignments, un `ToggleButtonGroup` permite switch-ul intre chain-ul `free` si `premium` pentru componenta curenta:
- Cand selectezi **FREE**, tabelul arata chain-ul primary+fallbacks din rows cu `tier='free'` (Qwen local primary + cloud fallbacks)
- Cand selectezi **PREMIUM**, tabelul arata chain-ul din rows cu `tier='premium'` (Gemini 3 Flash / Claude Sonnet primary + Qwen local safety)
- Badge-ul stage (`[FREE]` verde / `[PREMIUM]` galben) reflecta tier-ul curent selectat
- Daca un stage NU are rows premium configurate, apare un `Alert` warning si fallback automat la chain-ul `free`
- Editare: cand userul schimba un model in tabel, se aplica doar pe tier-ul curent selectat; salvarea itereaza ambele tiers si trimite PUT la `/api/providers/assignments/:id`
State-ul `selectedTier` e stored in componenta main LLMComponentsConfig; `StageAssignmentsPanel` primeste `selectedTier` ca prop si filtreaza `stage.modelsByTier[selectedTier]` la randare. Structura `modelsByTier: Record<'free'|'premium', ModelConfig[]>` e construita in `fetchStageAssignments()` prin grouping pe `tier` din response-ul GET /api/providers/assignments (care include acum campul `tier`).
Per componenta analiza (Techniques, AI Tampered, Claims, Source Assessment):
- **Stage Assignments**: modele LLM pe fiecare etapa (primary + 3 fallback-uri) — **cu toggle Free/Premium**
- **Vision Models**: cascade viziune (doar AI Tampered)
- **Available Models**: catalog modele din PG
- **Parameters**: scoring_config editabil — toti parametrii de calcul scor (din PG via didiFramework)
- Techniques: count_scaler, severe_threshold, intensity bonus, dimension/count bonuses
- AI Tampered: blend_weights, disclosure_impact, undisclosed_threshold, verdict thresholds
- Claims: status_weights (VT/LT/UV/OP/LF/VF), all_unverified behavior/credibility
- Source Assessment: axis_weights (publication/domain/author/platform), verdict_thresholds
- **Prompts**: system prompt + user template editabile
- **Test Analysis**: test cu text sample
Verdict (VerdictConfig) — 8 tab-uri:
- Component Weights (ponderile globale, fallback daca profilurile nu exista)
- Verdict Categories (RELIABLE..DISINFORMATION ranges, editabile)
- Risk Levels (VERY_LOW..CRITICAL ranges, editabile)
- Severity & Actions (LOW..CRITICAL + actiuni recomandate)
- Overrides & Synergy (configurare override-uri globale)
- Confidence (bonusuri/penalitati)
- Topic Multipliers (elections, health, climate — editabile)
- **Input Profiles** (NOU): ponderi per input type (text/image/audio/video/url), override-uri per profil, AI disclosure multipliers, reguli INCONCLUSIVE. Editabil cu save + sync to Redis.
Logica: src/components/LLMComponentsConfig/index.tsx
Verdict: src/components/LLMComponentsConfig/VerdictConfig.tsx
### ModerationQueue (/moderation)
Paginated table cu filtre (status: pending/in_review/resolved/all + priority CSV). Auto-refresh 30s pe pending tab. Click row -> detail. Top-right: link "View Stats".
Logica: src/components/Moderation/ModerationQueue.tsx
Polling: 30s (auto-refresh pe pending tab)
### ModerationDetail (/moderation/:queueId)
Side-by-side: Input (text/url + meta) | AI Verdict (risk_score, confidence, severity, components run/skipped). Card metadata queue cu enqueue_reason + meta JSONB. Action buttons:
- Claim for Review (pe pending)
- Approve as is / Resolve with Corrections / Reject (pe in_review)
Resolve dialog: action toggle + JSON corrections editor (validated client-side) + notes.
Logica: src/components/Moderation/ModerationDetail.tsx
### ModerationStats (/moderation/stats)
3 stat cards (pending, in_review, resolved_24h) cu colored borders. Open by Priority chips. Avg time in queue. Auto-refresh 30s.
Logica: src/components/Moderation/ModerationStats.tsx
---
## Autentificare (Keycloak)
### Configurare
Fisier: src/services/keycloak.ts
```
Client ID: admin-dashboard (din REACT_APP_KEYCLOAK_CLIENT_ID, obligatoriu)
Realm: didi-admins (din REACT_APP_KEYCLOAK_REALM, obligatoriu)
PKCE: S256 (obligatoriu)
URL: din REACT_APP_KEYCLOAK_URL (requireEnv) — pointeaza spre /auth pe nginx-ul dashboard-ului, care proxiaza catre Keycloak LOCAL didi-keycloak
```
### Flow
Fisier: src/contexts/AuthContext.tsx
1. La pornire: `keycloak.init({ onLoad: 'login-required' })`
2. Token stocat in localStorage.keycloak_token
3. Refresh token la fiecare 30s (updateToken(70) -- refresh daca expira in < 70s)
4. La 401 de la axios: event auth:unauthorized -> logout automat
5. REACT_APP_STAGING_MODE=true sare peste toata autentificarea
### Protectie rute
Fisier: src/components/auth/ProtectedRoute.tsx
- Verifica isAuthenticated inainte de a randa componenta
- Redirect la /login daca neautentificat
- Suporta requiredRole (verificare rol Keycloak)
- Suporta `requiredAnyRole?: string[]` (any-of) pentru rute partajate intre mai multe roluri staff
Fisier: src/components/auth/LoginRedirect.tsx -- apeleaza keycloak.login() imediat
Fisier: src/components/auth/Unauthorized.tsx -- 403 page (lista roluri necesare + "Go to Public App" + "Log out")
### Role gating (admin / moderator / senior_moderator)
Roluri definite pe Keycloak LOCAL `didi-keycloak`, realm `didi-admins`: `moderator`, `senior_moderator` (langa `admin` existent).
- `App.tsx`: AdminLayout wrapped in `ProtectedRoute requiredAnyRole=[admin, moderator, senior_moderator]` (end-users tip `viewer` primesc 403 -> Unauthorized page)
- Per-route guards:
- /framework, /users, /providers, /llm-components -> admin-only
- /history + /moderation/* -> admin OR moderator
- AdminLayout sidebar: sectiunea Configuration admin-only; sectiunea Management split (Users=admin, History=staff, Moderation=moderator-aware)
- Staging mode (`REACT_APP_STAGING_MODE=true`) face `hasRole()` sa returneze mereu true -> toate gates bypass pentru testare locala. In prod, Keycloak JWT roles enforcate strict.
### Context expus
AuthContext expune: isAuthenticated, initialized, user (tokenParsed), token, login(), logout(), hasRole(role)
---
## Configurare Nginx
### Productie (nginx-ssl.conf)
HTTPS pe port 443 (TLS 1.2/1.3) + HTTP pe port 80.
| Location | Proxy catre | Scop |
|----------|------------|------|
| / | SPA fallback /admin/index.html | React app |
| /api/ | http://didi-kong:8000 | Orchestrator API prin Kong LOCAL |
| /auth/ | http://keycloak:8080 | Keycloak LOCAL (container `didi-keycloak`); NU strip — Keycloak servit sub `/auth` (KC_HTTP_RELATIVE_PATH) |
| /agent-v3/ | http://didi-agent-v3:24803 | agent-v3 direct (strip prefix) |
| /framework/ | http://didi-framework:3005 | didiFramework direct (strip prefix) |
| /health-check/redis-commander | http://staging-dataLayer-redis-commander:8081 | Health check Redis Commander |
| /health-check/rabbitmq | http://staging-dataLayer-rabbitmq:15672 | Health check RabbitMQ |
| /health-check/minio | http://staging-dataLayer-minio:9000 | Health check MinIO |
| /health-check/pgadmin | http://staging-dataLayer-pgadmin:80 | Health check pgAdmin |
| /health-check/postgres | http://staging-dataLayer-postgres:5432 | Health check PostgreSQL |
Headere securitate: X-Frame-Options SAMEORIGIN, X-Content-Type-Options nosniff, X-XSS-Protection.
Cache static: 1 an + immutable pentru .js, .css, .png, .svg, .ico.
### Dev (nginx.conf)
Acelasi layout fara SSL, porturi 3000 si 80. Nu are proxy pentru /api/, /auth/, sau health checks (doar /agent-v3/ si /framework/).
---
## Comunicare real-time
### WebSocket (usePipelineProgress)
Fisier: src/hooks/usePipelineProgress.ts
Conectare la `wss://{host}/ws/runs/:runId/progress?include_outputs=true` pentru tracking progres pipeline.
Mesaje primite:
- connected -- confirmare subscriptie
- node_status -- update executie nod (key, status, label, type, timestamp, duration_ms)
- run_complete -- pipeline terminat (status: success/error/stopped, total_duration_ms)
- error -- mesaj eroare
Reconnect exponential backoff: 1s -> 2s -> 4s -> ... -> 30s max.
### Polling HTTP
| Componenta | Interval | Endpoint |
|-----------|----------|----------|
| ServicesDashboard | 30s | Health checks (8 endpoint-uri) |
| UniversalAnalysisDashboard | 2s | GET /api/v1/runs/:runId?include_live_status=true |
| UniversalAnalysisDashboard | 10s | GET /api/v1/runs?limit=50 |
| PipelineExecutor | 500ms | GET /api/v1/runs/:runId?include_live_status=true |
| UniversalResultsPage | 2s | GET /api/v1/runs/status/:runId |
| RunDetailsPage | 3s | GET /api/v1/runs/:runId/results |
| RunsHistory | 10s | GET /api/v1/runs (optional, toggle) |
| useAnalysisResults | 5s | GET /api/v1/runs/:id?include_live_status=true |
---
## Hooks custom
| Hook | Fisier | Scop |
|------|--------|------|
| useAnalysisResults | src/hooks/useAnalysisResults.ts | Rezultate analiza cu auto-refresh 5s, filtrare pe tags |
| useRunsHistory | src/hooks/useRunsHistory.ts | Istoric run-uri paginat (fetch(), nu axios), filtre status/date/search |
| usePipelineProgress | src/hooks/usePipelineProgress.ts | WebSocket progres pipeline, reconnect backoff |
| useDeleteDialog | src/hooks/useDeleteDialog.ts | State generic dialog confirmare stergere |
| useExpandedItems | src/hooks/useExpandedItems.ts | State accordion expandat (Set) |
| useTabState | src/hooks/useTabState.ts | State tab per item (Record) |
| useBenchmarkRunner | src/components/orchestrator/benchmark/useBenchmarkRunner.ts | Executie benchmark pipeline + polling status |
| useAutoLayout | src/components/orchestrator/hooks/useAutoLayout.ts | Auto-layout noduri ReactFlow |
| useDagValidation | src/components/orchestrator/hooks/useDagValidation.ts | Validare DAG (detectie cicluri) |
| useEdgeSuggestions | src/components/orchestrator/hooks/useEdgeSuggestions.ts | Sugestii edge-uri data-flow |
| useProviderModels | src/components/orchestrator/hooks/useProviderModels.ts | Fetch modele si resurse catalog |
| useResourceEndpoints | src/components/orchestrator/hooks/useResourceEndpoints.ts | Fetch resurse catalog |
---
## Tema (light/dark)
Fisier: src/theme/index.ts
Persistenta: localStorage['didi-admin-theme']
Toggle: src/components/ThemeToggle.tsx (buton in sidebar ServicesDashboard)
### Culori
| Rol | Valoare | Nume |
|-----|---------|------|
| Primary | #0052CC | Deep Trust Blue |
| Secondary | #00BFA6 | Honest Teal |
| Success | #28A745 | Truth Green |
| Error | #E63946 | Caution Red |
| Warning | #FF8C42 | Insight Orange |
| Background (light) | #F5F7FA | -- |
| Background (dark) | #121212 | -- |
| Text (light) | #1F2933 | Slate Charcoal |
### Tipografie
Font: Inter, Roboto, Helvetica, Arial
- h1: 32px/700, h2: 24px/600, h3: 20px/500, body1: 16px/400, button: 14px/500/uppercase
### Overrides MUI
- Button: radius 8, padding 12px 24px
- Card: radius 12, hover shadow
- TextField: primary border on focus
- Chip: rounded 16px
---
## Dependente principale (package.json)
| Pachet | Versiune | Scop |
|--------|----------|------|
| react | ^19.1.1 | UI framework |
| react-router-dom | ^7.7.1 | Routing |
| @mui/material | ^7.3.1 | Component library |
| axios | ^1.11.0 | HTTP client |
| keycloak-js | ^26.2.0 | Autentificare Keycloak |
| @react-keycloak/web | ^3.4.0 | React bindings Keycloak |
| reactflow | ^11.11.4 | Vizualizare DAG pipeline |
| recharts | ^3.1.0 | Grafice costuri |
| react-dnd | ^16.0.1 | Drag-and-drop |
| react-dropzone | ^14.3.8 | Upload fisiere |
| zod | ^3.25.76 | Validare schema |
| neverthrow | ^8.1.1 | Result types (Ok/Err) |
| ts-pattern | ^5.6.0 | Pattern matching |
---
## Configurare Docker
### Dockerfile (productie)
```
Stage 1: node:20-alpine
- npm install --legacy-peer-deps
- react-scripts build (20+ ARG-uri pentru REACT_APP_*)
- ARG REACT_APP_KEYCLOAK_REALM default = didi-admins (= realm-ul real); .env confirma aceeasi valoare
- ARG REACT_APP_STAGING_MODE=false (+ ENV explicit — CRA altfel bake-uia ARG default in loc sa citeasca .env)
Stage 2: nginx:alpine
- Copie build in /usr/share/nginx/html/admin si /usr/share/nginx/html
- Copie nginx-ssl.conf + certificate SSL
- Expose 443, 80
```
### Variabile de mediu (.env)
`<host-local>` = mașina de deployment (host-ul local). Valorile reale din `.env` (bake-uite in build la build time):
```
# General
REACT_APP_SERVER_HOST=<host-local>
REACT_APP_HOST=<host-local>
REACT_APP_API_BASE_URL=https://<host-local>:3001 # prin Kong LOCAL (admin nginx /api/ → didi-kong:8000)
REACT_APP_STAGING_MODE=false
# Keycloak — LOCAL, proxiat prin admin nginx la /auth/
REACT_APP_KEYCLOAK_URL=https://<host-local>:3001/auth
REACT_APP_KEYCLOAK_REALM=didi-admins
REACT_APP_KEYCLOAK_CLIENT_ID=admin-dashboard
# Kong Gateway (LOCAL)
REACT_APP_KONG_PROXY_PORT=443
REACT_APP_KONG_ADMIN_PORT=18101
REACT_APP_KONG_MANAGER_PORT=18102
# Porturi servicii (afisate pe service cards)
REACT_APP_KEYCLOAK_PORT=28000
REACT_APP_POSTGRES_PORT=5000
REACT_APP_PGADMIN_PORT=5050
REACT_APP_REDIS_PORT=6379
REACT_APP_COMMANDER_PORT=8081
REACT_APP_RABBITMQ_MGMT_PORT=15672
REACT_APP_MINIO_CONSOLE_PORT=9001
```
Nota: `REACT_APP_KEYCLOAK_URL` **este** folosit de `keycloak.ts` (`requireEnv`), pointand spre `/auth` pe nginx-ul dashboard-ului, care proxiaza catre Keycloak LOCAL `didi-keycloak`.
`REACT_APP_STAGING_MODE=false` (real) → autentificarea Keycloak e ACTIVA; cand ar fi `true`, s-ar sari peste toata autentificarea (doar pentru testare locala).
---
## Ce NU face
- Nu are backend propriu (doar nginx reverse proxy + SPA static)
- Nu scrie direct in baza de date (toate operatiile prin didiFramework sau orchestrator API)
- Nu proceseaza analize (doar le lanseaza si monitorizeaza)
- Nu gestioneaza fisiere (upload-urile merg prin orchestrator API -> MinIO)
- Nu are SSR (Server Side Rendering) -- e un SPA clasic
- Nu are teste unitare (doar un e2e dark-mode.spec.ts cu Playwright)
- CostDashboard exista dar nu e rutata (pagina orfana)
## Functionalitati suplimentare (in afara celor 9 module de caiet)
| Functionalitate | Unde in cod | Note |
|-----------------|-------------|------|
| CostDashboard (costuri LLM) | `src/pages/CostDashboard/` | Pagina orfana — NU e rutata in App.tsx; consuma `/api/v1/costs/*` de pe orchestrator |
(HIL Moderation din dashboard — ModerationQueue/Detail/Stats + ModerationSettings — este integrarea UI a Modulului de moderare din backend; ramane in sectiunile de mai sus.)
---
## Recent Changes (2026-05-05)
- **Keycloak login fix CRITICAL**: `src/services/keycloak.ts` ignora `REACT_APP_KEYCLOAK_URL` (folosea `${window.location.origin}/auth` hardcoded) — cauza "Cookie not found" cross-domain. Acum foloseste `requireEnv('REACT_APP_KEYCLOAK_URL')` direct.
- **`.env`**: `REACT_APP_STAGING_MODE=false` (era `true` -> bypass auth complet), `REACT_APP_KEYCLOAK_REALM=didi-admins` (era gresit `didi-clients`), `REACT_APP_KEYCLOAK_URL=https://<host-local>:3001/auth` (Keycloak LOCAL `didi-keycloak` prin admin nginx `/auth`; nu SSO cluster).
- **`Dockerfile`**: `ARG REACT_APP_STAGING_MODE=false` (era `true`) + `ENV REACT_APP_STAGING_MODE=$REACT_APP_STAGING_MODE` adaugat (CRA bake-uia ARG default in loc sa citeasca .env).
- **`Moderation/api.ts` + `ModerationSettings/api.ts`**: helper nou `authedFetch()` care injecteaza `Authorization: Bearer ${localStorage.keycloak_token}`. Inainte faceau `fetch()` fara auth -> 401 pe Kong.
- **Role-based UI guard `AdminLayout.tsx`**: Dashboard + service groups (Data Layer, Gateway Layer, etc.) acum vizibile doar pentru `admin`. Moderator/senior_moderator vad doar Management section (Analysis History + Moderation).
- **`App.tsx` `DefaultLanding`**: redirect automat la `/moderation` pentru non-admin (in loc de ServicesDashboard). Plus route `/dashboard` cere rol `admin`.
- **UI cosmetics**: `ModerationQueue.tsx` Status/Priority labels cu `display: block, mb: 0.5` (erau lipite de butoane); priority 1,2,3,4,5 (erau doar 1,3,4); `AdminLayout.tsx` sidebar logo + main navbar header ambele la `height: 64px` (linii divider aliniate); divider deasupra "Management" ascuns pentru non-admin.
---
## User Management overhaul (2026-05-05)
`UserManagement.tsx` complet rescris (3 tabs) + 2 componente noi + `UserEditModal.tsx` extins. Toate hook-uite la endpoint-urile noi din didiFramework `/api/admin/*`.
### Tab #1 — Users (extins)
Coloane: ☑ Checkbox (bulk), Email (+ keycloak_id snippet), Name, **Sync** chip (Synced/KC only — warning bg), Plan, **Group** chip, **Roles** chips (top-3 + "+N more"), Email Verified Switch, Active, Credits (tooltip "Spent: N"), **Storage** progress bar (>90% red / >70% warning), Actions (Edit modal / 🛡️ RolesDialog / 📜 UsageHistory / 🗑️ Delete hard).
Filter row: search + Plan + **Sync filter** (All/Synced/Keycloak only).
Bulk toolbar (apare pe selectare): activate / deactivate / mark email verified / set credits to N / delete. Sequential per id (evită hammer Keycloak).
### Tab #2 — Subscription Plans (nemodificat)
### Tab #3 — Audit Log (NOU)
Citește `/api/admin/audit-log` paginat. Filtre Action preset + Actor email (ILIKE) + Since (datetime-local). Coloane: When / Action chip color-coded / Target / Actor / Payload (truncat + tooltip JSON pretty) / IP.
### Componente noi
| File | Rol |
|------|-----|
| `RolesDialog.tsx` | Multi-select check-list pe realm roles, diff summary (add/remove), save → `PUT /:id/roles`. |
| `UsageHistoryModal.tsx` | Last 100 rows din `bos_sysadmin.ai_credit_usage`. Defensive render (input_type chip + credits + session_id + JSON fallback pe coloane necunoscute). |
### `UserEditModal.tsx` extins
În plus față de existent: **Group select** (dropdown grupuri Keycloak, "(no group)" = remove) + **"Send password reset email" button** (apelează `POST /:id/reset-password` lifespan 86400s, Snackbar succes).
### `SensitiveTopicsCard.tsx` extins (Phase D1)
Rânduri rich cu volatility chip color-coded (volatile=red, evolving=warning, stable=success) + TTL/recency chips + iconul ⚙️ Tune → `EditVolatilityDialog` (Select tier auto-seed defaults + override fine pe ttl/recency/half_life). Add-form extins cu Volatility select.
### Live state
20 users live. Reset-password fail cunoscut: cluster Keycloak SMTP not configured (502 cu detail în audit_log) — operațional, nu cod.