diff --git a/src/app/dashboard/trust/outcomes/page.tsx b/src/app/dashboard/trust/outcomes/page.tsx new file mode 100644 index 0000000..ecc9753 --- /dev/null +++ b/src/app/dashboard/trust/outcomes/page.tsx @@ -0,0 +1,133 @@ +'use client'; + +import { useMemo, useState } 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 Goal { id: string; title: string; description: string | null; status: string; progress: number | null; tags: string[]; targetDate: string | null; updatedAt: string; createdAt: string; } +interface Contract { id: string; title: string; status: string; value: number | null; currency: string | null; counterpartyName: string | null; endDate: string | null; } + +const PORTFOLIO_TAGS = ['project', 'proiect', 'product', 'produs', 'launch', 'lansare', 'client', 'outcome']; + +export default function TrustOutcomesPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const [view, setView] = useState<'goals' | 'contracts'>('goals'); + + const { data: goals = [] } = useQuery({ queryKey: ['to-goals', tenantId], queryFn: () => apiFetch('/v1/goals?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 }); + const { data: contracts = [] } = useQuery({ queryKey: ['to-contracts', tenantId], queryFn: () => apiFetch('/v1/contracts?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 }); + + const portfolio = useMemo(() => { + const projectGoals = goals.filter((g) => + g.status === 'completed' || g.tags.some((t) => PORTFOLIO_TAGS.includes(t.toLowerCase())) + ).sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); + + const deliveredContracts = contracts.filter((c) => ['signed', 'active', 'completed', 'delivered'].includes(c.status)) + .sort((a, b) => (b.value ?? 0) - (a.value ?? 0)); + + const totalContractValue = deliveredContracts.reduce((s, c) => s + (c.value ?? 0), 0); + const currencies = [...new Set(deliveredContracts.map((c) => c.currency).filter(Boolean))]; + + return { projectGoals, deliveredContracts, totalContractValue, currencies }; + }, [goals, contracts]); + + return ( +
+
+ +

Proiecte & Rezultate

+

Portofoliu de obiective finalizate și contracte livrate.

+
+ + {/* Summary */} +
+
+

{portfolio.projectGoals.length}

+

proiecte / obiective

+
+
+

{portfolio.deliveredContracts.length}

+

contracte livrate

+
+
+

+ {portfolio.totalContractValue > 0 + ? `${portfolio.totalContractValue.toLocaleString()} ${portfolio.currencies[0] ?? ''}` + : '—'} +

+

valoare contracte

+
+
+ + {/* Toggle */} +
+ {(['goals', 'contracts'] as const).map((v) => ( + + ))} +
+ + {view === 'goals' ? ( + portfolio.projectGoals.length === 0 ? ( +
+

🎯

+

Niciun obiectiv finalizat sau cu tag de proiect.

+
+ ) : ( +
+ {portfolio.projectGoals.map((g) => ( +
+
+

{g.title}

+ + {g.status} + +
+ {g.description &&

{g.description}

} + {g.progress !== null && ( +
+
+
+ )} +

+ {new Date(g.updatedAt).toLocaleDateString('ro-RO', { month: 'short', year: 'numeric' })} +

+
+ ))} +
+ ) + ) : ( + portfolio.deliveredContracts.length === 0 ? ( +
+

📄

+

Niciun contract în portofoliu.

+
+ ) : ( +
+ {portfolio.deliveredContracts.map((c) => ( +
+
+

{c.title}

+
+ {c.counterpartyName && {c.counterpartyName}} + {c.endDate && {new Date(c.endDate).toLocaleDateString('ro-RO', { month: 'short', year: 'numeric' })}} +
+
+
+ {c.value ?

{c.value.toLocaleString()} {c.currency}

: null} + {c.status} +
+
+ ))} +
+ ) + )} +
+ ); +}