diff --git a/src/app/dashboard/reports/page.tsx b/src/app/dashboard/reports/page.tsx new file mode 100644 index 0000000..9e98d4c --- /dev/null +++ b/src/app/dashboard/reports/page.tsx @@ -0,0 +1,155 @@ +'use client'; + +import Link from 'next/link'; +import { useQuery } from '@tanstack/react-query'; +import { apiFetch } from '../../../lib/api'; +import type { Goal, Task, Decision, Transaction } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +const B = '/dashboard'; + +interface BriefingToday { + date: string; + sections: { title: string; items: string[] }[]; +} + +export default function ReportsPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + + const opts = { enabled: Boolean(tenantId), staleTime: 60_000 }; + + const { data: goals = [] } = useQuery({ + queryKey: ['reports-goals', tenantId], + queryFn: () => apiFetch('/v1/goals', { tenantId }), + ...opts, + }); + const { data: tasks = [] } = useQuery({ + queryKey: ['reports-tasks', tenantId], + queryFn: () => apiFetch('/v1/tasks', { tenantId }), + ...opts, + }); + const { data: decisions = [] } = useQuery({ + queryKey: ['reports-decisions', tenantId], + queryFn: () => apiFetch('/v1/decisions', { tenantId }), + ...opts, + }); + const { data: transactions = [] } = useQuery({ + queryKey: ['reports-transactions', tenantId], + queryFn: () => apiFetch('/v1/transactions', { tenantId }), + ...opts, + }); + const { data: briefing } = useQuery({ + queryKey: ['reports-briefing', tenantId], + queryFn: () => apiFetch('/v1/briefing/today', { tenantId }), + ...opts, + }); + + const now = new Date(); + const activeGoals = goals.filter((g) => g.status === 'active' || g.status === 'on_track'); + const overdueTasks = tasks.filter((t) => t.dueDate && new Date(t.dueDate) < now && t.status !== 'done'); + const pendingDecisions = decisions.filter((d) => !d.selectedOption); + const thisMonthTx = transactions.filter((t) => t.transactionDate.startsWith(now.toISOString().slice(0, 7))); + const thisMonthRevenue = thisMonthTx + .filter((t) => t.type === 'income' || t.type === 'revenue') + .reduce((s, t) => s + Number(t.amountMinorUnits), 0); + + const REPORT_LINKS = [ + { label: 'Business Reports', href: `${B}/reports/business`, desc: 'Proiecte, pipeline, venituri, clienți.' }, + { label: 'Financial Reports', href: `${B}/reports/financial`, desc: 'Tranzacții, cash flow, conturi.' }, + { label: 'Accountant Reports', href: `${B}/reports/accountant`, desc: 'Pachet contabil cu dovezi și gaps.' }, + { label: 'Decision Reports', href: `${B}/reports/decisions`, desc: 'Decizii, scenarii, estimări.' }, + ]; + + return ( +
+
+

Rapoarte Executive

+

+ Sumar executiv generat automat din datele platformei.{' '} + {briefing?.date && ( + Briefing: {new Date(briefing.date).toLocaleDateString('ro-RO', { weekday: 'long', day: 'numeric', month: 'long' })} + )} +

+
+ + {/* KPI snapshot */} +
+ {[ + { label: 'Obiective active', value: activeGoals.length, color: 'text-ink', href: `${B}/goals` }, + { label: 'Task-uri restante', value: overdueTasks.length, color: overdueTasks.length > 0 ? 'text-signal-danger' : 'text-ink', href: `${B}/tasks` }, + { label: 'Decizii în așteptare', value: pendingDecisions.length, color: pendingDecisions.length > 0 ? 'text-signal-warn' : 'text-ink', href: `${B}/decisions` }, + { label: 'Venituri luna curentă', value: thisMonthRevenue > 0 ? `${(thisMonthRevenue / 100).toLocaleString('ro-RO')}` : '—', color: 'text-signal-ok', href: `${B}/finance` }, + ].map((kpi) => ( + +

{kpi.label}

+

{kpi.value}

+ + ))} +
+ + {/* Briefing sections */} + {briefing?.sections && briefing.sections.length > 0 && ( +
+

+ Briefing zilnic +

+
+ {briefing.sections.map((sec, i) => ( +
+

{sec.title}

+
    + {sec.items.map((item, j) => ( +
  • · {item}
  • + ))} +
+
+ ))} +
+
+ )} + + {/* Goals status */} + {goals.length > 0 && ( +
+
+

+ Obiective ({goals.length}) +

+ Vezi toate → +
+
    + {goals.slice(0, 5).map((g) => ( +
  • +

    {g.title}

    + {g.status.replace(/_/g, ' ')} +
  • + ))} +
+
+ )} + + {/* Report type links */} +
+

+ Tipuri de rapoarte +

+
+ {REPORT_LINKS.map((r) => ( + +
+

{r.label}

+

{r.desc}

+
+ + + ))} +
+
+
+ ); +}