diff --git a/src/app/dashboard/digital-twin/page.tsx b/src/app/dashboard/digital-twin/page.tsx new file mode 100644 index 0000000..d6f68b8 --- /dev/null +++ b/src/app/dashboard/digital-twin/page.tsx @@ -0,0 +1,116 @@ +'use client'; + +import { useMemo } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import Link from 'next/link'; +import { apiFetch } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +interface Decision { id: string; title: string; impact: string | null; status: string; rationale: string | null; tags: string[]; createdAt: string; } +interface Observation { id: string; metric: string; value: string; subjectType: string; createdAt: string; observedAt: string | null; } +interface Task { id: string; status: string; priority: string | null; tags: string[]; createdAt: string; } + +const STYLE_INDICATORS = [ + { id: 'decisiveness', label: 'Decisivitate', desc: 'Cât de rapid și ferm iei decizii' }, + { id: 'risk', label: 'Toleranță la risc', desc: 'Preferința pentru soluții conservatoare vs. îndrăznețe' }, + { id: 'planning', label: 'Orientare spre planificare', desc: 'Task-uri cu due date vs. spontane' }, + { id: 'collaboration', label: 'Orientare colaborativă', desc: 'Decizii cu stakeholders vs. autonome' }, + { id: 'documentation', label: 'Documentare', desc: 'Cât de detaliat documentezi deciziile și observațiile' }, + { id: 'consistency', label: 'Consistență', desc: 'Regularitatea logării datelor personale' }, +]; + +export default function DigitalTwinPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + + const { data: decisions = [] } = useQuery({ queryKey: ['dt-dec', tenantId], queryFn: () => apiFetch('/v1/decisions?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 }); + const { data: observations = [] } = useQuery({ queryKey: ['dt-obs', tenantId], queryFn: () => apiFetch('/v1/observations', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 }); + const { data: tasks = [] } = useQuery({ queryKey: ['dt-tasks', tenantId], queryFn: () => apiFetch('/v1/tasks?limit=500', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 }); + + const profile = useMemo(() => { + const totalDecisions = decisions.length; + const documentedDecisions = decisions.filter((d) => d.rationale && d.rationale.length > 20).length; + const highImpact = decisions.filter((d) => d.impact === 'high' || d.impact === 'critical').length; + const tasksWithDue = tasks.filter((t) => t.dueDate !== undefined).length; + const urgentTasks = tasks.filter((t) => t.priority === 'urgent').length; + const obsLast30 = observations.filter((o) => (Date.now() - new Date(o.observedAt ?? o.createdAt).getTime()) < 30 * 86400_000).length; + + const scores: Record = { + decisiveness: Math.min(100, totalDecisions * 5), + risk: highImpact > 0 && totalDecisions > 0 ? Math.min(100, Math.round((highImpact / totalDecisions) * 200)) : 30, + planning: tasks.length > 0 ? Math.min(100, Math.round((tasksWithDue / tasks.length) * 100)) : 50, + collaboration: totalDecisions > 0 ? Math.min(100, Math.round((documentedDecisions / totalDecisions) * 100)) : 0, + documentation: totalDecisions > 0 ? Math.min(100, Math.round((documentedDecisions / totalDecisions) * 100)) : 0, + consistency: Math.min(100, obsLast30 * 5), + }; + + return scores; + }, [decisions, observations, tasks]); + + return ( +
+
+

Digital Twin

+

Model de preferințe și stil decizional — derivat din comportamentul tău în CEO OS.

+
+ +
+

+ Limitări obligatorii: Modelul este derivat exclusiv din datele auto-raportate în CEO OS. + Nu predice comportamentul viitor. Nu actionează autonom. Explică probabilități observate, nu certitudini. + Scorurile sunt orientative — nu se folosesc pentru evaluarea altor persoane. +

+
+ + {/* Style profile */} +
+

Profil stil decizional

+ {STYLE_INDICATORS.map((ind) => { + const score = profile[ind.id] ?? 0; + return ( +
+
+
+

{ind.label}

+

{ind.desc}

+
+ {score}% +
+
+
+
+
+ ); + })} +
+ + {/* Context */} +
+
+

{decisions.length}

+

decizii logate

+
+
+

{observations.length}

+

observații personale

+
+
+

{tasks.length}

+

taskuri în sistem

+
+
+ +
+

Îmbunătățește modelul

+
+

Cu cât loghezi mai multe decizii cu rațional și observații personale, cu atât modelul devine mai precis.

+
+
+ Adaugă decizii → + Loghează observații → + Performance Coach → +
+
+
+ ); +}