diff --git a/src/app/dashboard/business/page.tsx b/src/app/dashboard/business/page.tsx new file mode 100644 index 0000000..5b9595e --- /dev/null +++ b/src/app/dashboard/business/page.tsx @@ -0,0 +1,234 @@ +'use client'; + +import Link from 'next/link'; +import { useQuery } from '@tanstack/react-query'; +import { apiFetch, type Goal, type Transaction, type Decision } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +interface ExecDash { + status: 'ready' | 'building'; + counters: { + openTasks: number; + overdueTasks: number; + deadlinesNext7Days: number; + organizations: number; + segments: number; + researchBriefs: number; + } | null; + generatedAt: string | null; +} + +export default function BusinessOverviewPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + + const { data: exec } = useQuery({ + queryKey: ['exec-dash-biz', tenantId], + queryFn: () => apiFetch('/v1/dashboard/executive', { tenantId }), + enabled: Boolean(tenantId), + staleTime: 60_000, + }); + + const { data: goals = [] } = useQuery({ + queryKey: ['goals-biz', tenantId], + queryFn: () => apiFetch('/v1/goals', { tenantId }), + enabled: Boolean(tenantId), + }); + + const { data: transactions = [] } = useQuery({ + queryKey: ['transactions-biz', tenantId], + queryFn: () => apiFetch('/v1/transactions', { tenantId }), + enabled: Boolean(tenantId), + }); + + const { data: decisions = [] } = useQuery({ + queryKey: ['decisions-biz', tenantId], + queryFn: () => apiFetch('/v1/decisions', { tenantId }), + enabled: Boolean(tenantId), + }); + + const c = exec?.counters; + const goalsAtRisk = goals.filter((g) => g.status === 'at_risk').length; + const txMissing = transactions.filter((t) => t.evidenceStatus === 'missing').length; + const decisionsPending = decisions.filter((d) => !d.selectedOption).length; + + const modules = [ + { + label: 'Organizații', + value: c?.organizations ?? '—', + href: '/dashboard/organizations', + badge: null, + badgeColor: '', + }, + { + label: 'Sarcini deschise', + value: c?.openTasks ?? '—', + href: '/dashboard/tasks', + badge: c?.overdueTasks ? `${c.overdueTasks} restante` : null, + badgeColor: 'bg-signal-danger text-white', + }, + { + label: 'Obiective active', + value: goals.length, + href: '/dashboard/goals', + badge: goalsAtRisk > 0 ? `${goalsAtRisk} la risc` : null, + badgeColor: 'bg-signal-warn text-white', + }, + { + label: 'Tranzacții', + value: transactions.length, + href: '/dashboard/transactions', + badge: txMissing > 0 ? `${txMissing} fără dovadă` : null, + badgeColor: 'bg-signal-warn text-white', + }, + { + label: 'Decizii pending', + value: decisionsPending, + href: '/dashboard/decisions', + badge: null, + badgeColor: '', + }, + { + label: 'Segmente', + value: c?.segments ?? '—', + href: '/dashboard/segments', + badge: null, + badgeColor: '', + }, + { + label: 'Research briefs', + value: c?.researchBriefs ?? '—', + href: '/dashboard/research', + badge: null, + badgeColor: '', + }, + { + label: 'Scadențe 7 zile', + value: c?.deadlinesNext7Days ?? '—', + href: '/dashboard/tasks', + badge: null, + badgeColor: '', + }, + ]; + + return ( +
+
+

Vedere Generală Business

+

+ Sumar executiv al tuturor modulelor CEO-OS. + {exec?.generatedAt && ( + <> Actualizat: {new Date(exec.generatedAt).toLocaleString('ro-RO')}. + )} +

+
+ + {exec?.status === 'building' && ( +
+

+ Proiecția executive se construiește. Contoarele vor apărea în câteva secunde. +

+
+ )} + +
+ {modules.map(({ label, value, href, badge, badgeColor }) => ( + +

+ {label} +

+

{value}

+ {badge && ( + + {badge} + + )} + + ))} +
+ +
+
+

+ Obiective +

+ {goals.length === 0 ? ( +

Niciun obiectiv definit.

+ ) : ( +
    + {goals.slice(0, 5).map((g) => { + const dot = + g.status === 'on_track' + ? 'bg-signal-ok' + : g.status === 'at_risk' + ? 'bg-signal-warn' + : g.status === 'achieved' + ? 'bg-bronze-deep' + : g.status === 'abandoned' + ? 'bg-signal-danger' + : 'bg-paper-sunken'; + return ( +
  • + + {g.metric} + {g.target} +
  • + ); + })} +
+ )} + + Toate obiectivele → + +
+ +
+

+ Tranzacții recente +

+ {transactions.length === 0 ? ( +

Nicio tranzacție înregistrată.

+ ) : ( +
    + {transactions.slice(0, 5).map((t) => ( +
  • + {t.type} + + {new Intl.NumberFormat('ro-RO', { + style: 'currency', + currency: t.currency, + }).format(Number(t.amountMinorUnits) / 100)} + + + {t.evidenceStatus} + +
  • + ))} +
+ )} + + Toate tranzacțiile → + +
+
+
+ ); +}