diff --git a/src/app/dashboard/privacy/classification/page.tsx b/src/app/dashboard/privacy/classification/page.tsx new file mode 100644 index 0000000..a5e6927 --- /dev/null +++ b/src/app/dashboard/privacy/classification/page.tsx @@ -0,0 +1,136 @@ +'use client'; + +import Link from 'next/link'; +import { useQuery } from '@tanstack/react-query'; +import { apiFetch } from '../../../../lib/api'; +import { useSession } from '../../../../components/session-provider'; + +interface Document { + id: string; filename: string; classification: string; + ocrStatus: string | null; expiresAt: string | null; createdAt: string; +} + +const CLASS_META: Record = { + C0: { label: 'C0 — Public', desc: 'Informații publice, fără restricții', cls: 'text-sky-500 bg-sky-500/10 border-sky-500/20', gdpr: 'Fără restricții GDPR' }, + C1: { label: 'C1 — Intern', desc: 'Uz intern, distribuire controlată', cls: 'text-signal-ok bg-signal-ok/10 border-signal-ok/20', gdpr: 'Art. 5 — Integritate' }, + C2: { label: 'C2 — Confidențial', desc: 'Date comerciale sensibile, parteneri', cls: 'text-signal-warn bg-signal-warn/10 border-signal-warn/20', gdpr: 'Art. 5 — Confidențialitate' }, + C3: { label: 'C3 — Restricționat',desc: 'Date personale, financiare, juridice', cls: 'text-orange-500 bg-orange-500/10 border-orange-500/20', gdpr: 'Art. 9 — Date speciale' }, + C4: { label: 'C4 — Secret', desc: 'NDA, secrete comerciale, date biometrice sau medicale', cls: 'text-signal-danger bg-signal-danger/10 border-signal-danger/20', gdpr: 'Art. 9 + DPA obligatoriu' }, +}; + +export default function ClassificationPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + + const { data: docs = [], isLoading } = useQuery({ + queryKey: ['docs-class', tenantId], + queryFn: () => apiFetch('/v1/documents?limit=500', { tenantId }), + enabled: Boolean(tenantId), + staleTime: 120_000, + }); + + const total = docs.length; + const byClass: Record = {}; + for (const d of docs) { + const k = d.classification ?? 'C1'; + byClass[k] = [...(byClass[k] ?? []), d]; + } + + const highRisk = (byClass['C3'] ?? []).length + (byClass['C4'] ?? []).length; + const ocrPending = docs.filter((d) => d.ocrStatus === 'pending').length; + const withExpiry = docs.filter((d) => d.expiresAt).length; + + return ( +
+
+

Clasificare Date

+

+ {isLoading ? 'Se încarcă…' : `${total} documente · ${highRisk} cu risc ridicat (C3/C4)`} +

+
+ + {/* Quick stats */} +
+
+

{total}

+

Total documente

+
+
0 ? 'bg-signal-danger/5' : ''}`}> +

0 ? 'text-signal-danger' : 'text-ink'}`}>{highRisk}

+

C3/C4 (risc ridicat)

+
+
+

{withExpiry}

+

Cu dată expirare

+
+
+ + {isLoading ? ( +
Se încarcă…
+ ) : total === 0 ? ( +
+

📁

+

Niciun document în bibliotecă.

+ Accesează biblioteca → +
+ ) : ( +
+ {(['C4','C3','C2','C1','C0'] as const).map((cls) => { + const items = byClass[cls] ?? []; + const meta = CLASS_META[cls]; + const pct = total > 0 ? Math.round(items.length / total * 100) : 0; + if (items.length === 0) return null; + return ( +
c.startsWith('border-')) ?? 'border-border'}`}> +
+
+

{meta.label}

+

{meta.desc}

+
+
+

{items.length}

+

{pct}% din total

+
+
+ + {/* Progress bar */} +
+
c.startsWith('bg-')) ?? 'bg-primary'}`} + style={{ width: `${pct}%` }} /> +
+ + {/* GDPR note */} +

⚖ {meta.gdpr}

+ + {/* Recent docs sample */} +
+ {items.slice(0, 5).map((d) => ( +
+ 📄 + {d.filename} + {d.expiresAt && exp: {d.expiresAt.slice(0, 10)}} +
+ ))} + {items.length > 5 && ( +

și alte {items.length - 5} documente…

+ )} +
+
+ ); + })} +
+ )} + + {ocrPending > 0 && ( +
+

+ ⚠ {ocrPending} documente au OCR pendente — clasificarea poate fi incompletă.{' '} + Revizuiește OCR → +

+
+ )} +
+ ); +}