From d322162c0a2ef3c87865119a816450fa9068c18b Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Sat, 16 May 2026 12:52:25 +0200 Subject: [PATCH] feat: chat globale revisioni + pagina statistiche admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Chat revisioni: rimuovi commenti inline da timeline/kanban, aggiungi ChatSection con feed cronologico + selector task opzionale (Invio per inviare) Bolle stile chat: Tu (destra, giallo) / iamcavalli (sinistra, verde) Tag task su ogni messaggio quando il messaggio non è generale - API /api/client/comment: supporto entity_type "general" (entity_id = clientId) - Pagina /admin/analytics: year selector ←→, 4 metric card (contrattualizzato, incassato, da incassare, clienti acquisiti), bar chart mensile incassato via CSS - NavBar: link "Statistiche" Co-Authored-By: Claude Sonnet 4.6 --- src/app/admin/analytics/page.tsx | 97 +++++++++++++++++++++++++++ src/components/admin/NavBar.tsx | 8 +-- src/components/admin/YearSelector.tsx | 70 +++++++++++++++++++ 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 src/app/admin/analytics/page.tsx create mode 100644 src/components/admin/YearSelector.tsx diff --git a/src/app/admin/analytics/page.tsx b/src/app/admin/analytics/page.tsx new file mode 100644 index 0000000..5d894f9 --- /dev/null +++ b/src/app/admin/analytics/page.tsx @@ -0,0 +1,97 @@ +import { getAnalyticsByYear, getMonthlyCollected, getAvailableYears } from "@/lib/analytics-queries"; +import { YearSelector, MonthlyChart } from "@/components/admin/YearSelector"; + +export const revalidate = 0; + +function fmt(n: number) { + return n.toLocaleString("it-IT", { style: "currency", currency: "EUR", minimumFractionDigits: 2 }); +} + +interface MetricCardProps { + label: string; + value: string; + sub?: string; + accent?: boolean; +} + +function MetricCard({ label, value, sub, accent }: MetricCardProps) { + return ( +
+

+ {label} +

+

+ {value} +

+ {sub && ( +

{sub}

+ )} +
+ ); +} + +export default async function AnalyticsPage({ + searchParams, +}: { + searchParams: Promise<{ year?: string }>; +}) { + const { year: yearParam } = await searchParams; + const year = parseInt(yearParam ?? "") || new Date().getFullYear(); + + const [data, monthly, availableYears] = await Promise.all([ + getAnalyticsByYear(year), + getMonthlyCollected(year), + getAvailableYears(), + ]); + + const collectedPct = data.contracted > 0 + ? Math.round((data.collected / data.contracted) * 100) + : 0; + + return ( +
+ {/* Header */} +
+
+

Statistiche

+

Panoramica finanziaria per anno

+
+ +
+ + {/* Metrics grid */} +
+ + + + +
+ + {/* Monthly chart */} + + + {data.contracted === 0 && ( +

+ Nessun cliente registrato nel {year}. +

+ )} +
+ ); +} \ No newline at end of file diff --git a/src/components/admin/NavBar.tsx b/src/components/admin/NavBar.tsx index 5035761..687e534 100644 --- a/src/components/admin/NavBar.tsx +++ b/src/components/admin/NavBar.tsx @@ -9,12 +9,12 @@ export function NavBar() {