--- phase: 18-cleanup-consolidamento plan: 02 type: execute wave: 1 depends_on: [] files_modified: - src/app/admin/page.tsx - src/app/admin/analytics/page.tsx - src/components/admin/YearSelector.tsx autonomous: true requirements: - CLEAN-03 must_haves: truths: - "La Dashboard /admin mostra le statistiche annuali (MetricCard fatturato, grafico mensile, time tracking)" - "La rotta /admin/analytics non esiste più (404)" - "Il selettore anno naviga a /admin?year=X, non a /admin/analytics?year=X" - "Non esiste più il file src/app/admin/analytics/page.tsx" artifacts: - path: "src/app/admin/page.tsx" provides: "Dashboard con sezione Statistiche integrata" contains: "getAnalyticsByYear" - path: "src/app/admin/analytics/page.tsx" provides: "File eliminato" - path: "src/components/admin/YearSelector.tsx" provides: "YearSelector che naviga a /admin?year=Y" contains: "router.push(`/admin?year=${y}`)" key_links: - from: "src/app/admin/page.tsx" to: "lib/analytics-queries" via: "import getAnalyticsByYear, getMonthlyCollected, getAvailableYears, getTimeByClient, getTotalTrackedHours" pattern: "analytics-queries" - from: "YearSelector" to: "/admin" via: "router.push" pattern: "router.push.*admin" --- Fondere le statistiche di /admin/analytics nella Dashboard /admin ed eliminare la rotta duplicata. Purpose: CLEAN-03 — unica vista admin per statistiche. Elimina il doppione /admin/analytics e porta tutto il suo contenuto (MetricCard economiche + MonthlyChart + time tracking per cliente) nella dashboard esistente, sotto le KPI card e il feed attività già presenti. Output: /admin mostra statistiche annuali, /admin/analytics → 404, YearSelector aggiornato. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md Imports da copiare in admin/page.tsx: ```typescript import { getAnalyticsByYear, getMonthlyCollected, getAvailableYears, getTimeByClient, getTotalTrackedHours, } from "@/lib/analytics-queries"; import { YearSelector, MonthlyChart } from "@/components/admin/YearSelector"; ``` Funzioni helper da copiare in admin/page.tsx: ```typescript function fmtEur(n: number) { /* formatta in EUR it-IT */ } function fmtSeconds(s: number): string { /* h m format */ } ``` ATTENZIONE: admin/page.tsx ha già una funzione `fmtEur` (ma accetta `string`, non `number`). Rinominare una delle due o unificarle — preferire la versione che accetta `number` (più robusta) e aggiornare i consumer nel file. Componente MetricCard (da analytics/page.tsx) — da spostare in admin/page.tsx: ```typescript function MetricCard({ label, value, sub, accent }: { label: string; value: string; sub?: string; accent?: boolean; }) { ... } ``` Questo componente è diverso da KpiCard già presente in admin/page.tsx: KpiCard ha icona + colore; MetricCard ha stile accent verde o bianco con bordo. Mantieni ENTRAMBI (scopi diversi): KpiCard per le 4 metriche operative in cima, MetricCard per le statistiche economiche annuali nella nuova sezione. Firma del componente analytics/page.tsx (async, searchParams): ```typescript export default async function AnalyticsPage({ searchParams, }: { searchParams: Promise<{ year?: string }>; }) { const { year: yearParam } = await searchParams; const year = parseInt(yearParam ?? "") || new Date().getFullYear(); ... } ``` La stessa logica `year` va aggiunta al componente AdminDashboard in admin/page.tsx. Task 1: Integra statistiche annuali nella Dashboard (CLEAN-03) - src/app/admin/page.tsx — struttura attuale (KpiCard, FollowUpWidget, activity feed, fmtEur esistente) - src/app/admin/analytics/page.tsx — tutto il contenuto da portare (già in contesto da discovery) - src/components/admin/YearSelector.tsx — YearSelector e MonthlyChart (già in contesto da discovery) src/app/admin/page.tsx, src/components/admin/YearSelector.tsx STEP 1 — Aggiorna AdminDashboard in src/app/admin/page.tsx: a) Aggiungi alla firma del componente il parametro searchParams (come in analytics/page.tsx): ```typescript export default async function AdminDashboard({ searchParams, }: { searchParams: Promise<{ year?: string }>; }) { const { year: yearParam } = await searchParams; const year = parseInt(yearParam ?? "") || new Date().getFullYear(); ``` b) Aggiungi gli import mancanti in cima al file: ```typescript import { getAnalyticsByYear, getMonthlyCollected, getAvailableYears, getTimeByClient, getTotalTrackedHours, } from "@/lib/analytics-queries"; import { YearSelector, MonthlyChart } from "@/components/admin/YearSelector"; ``` c) Aggiungi le query analytics al Promise.all esistente (o crea un secondo await per separare le query di diversa frequenza — è accettabile avere due await distinti per chiarezza): ```typescript const [data, monthly, availableYears, timeByClient, totalHours] = await Promise.all([ getAnalyticsByYear(year), getMonthlyCollected(year), getAvailableYears(), getTimeByClient(year), getTotalTrackedHours(year), ]); ``` d) Copia la funzione MetricCard da analytics/page.tsx nel file (accanto a KpiCard). e) Risolvi il conflitto fmtEur: - La versione attuale in admin/page.tsx accetta `string` e fa `parseFloat(val)`. - Quella in analytics/page.tsx accetta `number`. - Unifica in una sola funzione che accetta `number`, aggiorna i consumer esistenti nel file che passavano una stringa (es. `fmtEur(kpi.revenueTotale)` e `fmtEur(kpi.pagamentiInSospeso)`) convertendo a number dove necessario, oppure mantieni entrambe con nomi distinti (`fmtEurStr` / `fmtEurNum`). Scegli l'approccio più pulito senza rompere i KPI card esistenti. f) Aggiungi la sezione Statistiche DOPO il feed attività esistente (mantieni tutto quello che c'era prima intatto — FollowUpWidget, KPI card, activity feed): ```tsx {/* ── SEZIONE STATISTICHE ANNUALI ── */}

Statistiche

Panoramica per anno

{/* Sezione economica */}

Fatturato

{/* Sezione time tracking */}

Tempo tracciato

{/* ... tabella ore per cliente (copia da analytics/page.tsx) ... */}
``` Ricopia fedelmente il blocco "ore per cliente" da analytics/page.tsx (righe 127-160) nella sezione time tracking della dashboard. Calcola collectedPct prima del return: ```typescript const collectedPct = data.contracted > 0 ? Math.round((data.collected / data.contracted) * 100) : 0; const maxClientSeconds = timeByClient[0]?.totalSeconds ?? 1; ``` STEP 2 — Aggiorna YearSelector per navigare verso /admin: In src/components/admin/YearSelector.tsx, riga ~18: ```typescript // PRIMA: router.push(`/admin/analytics?year=${y}`); // DOPO: router.push(`/admin?year=${y}`); ``` NON modificare: MonthlyChart (rimane nello stesso file), struttura esistente della dashboard (FollowUpWidget, KPI card, activity feed — tutto invariato sopra la nuova sezione).
grep "getAnalyticsByYear\|getMonthlyCollected\|getAvailableYears" /Users/simonecavalli/Vault/IAMCAVALLI/src/app/admin/page.tsx | wc -l Risultato atteso: 3 (tutte e tre le query importate e usate). Verifica YearSelector: grep "router.push" /Users/simonecavalli/Vault/IAMCAVALLI/src/components/admin/YearSelector.tsx Risultato atteso: contiene "/admin?year=" e NON "/admin/analytics". - src/app/admin/page.tsx importa da @/lib/analytics-queries - src/app/admin/page.tsx importa YearSelector e MonthlyChart - YearSelector.tsx naviga a /admin?year=Y (non a /admin/analytics) - Il file compila senza errori TypeScript La Dashboard mostra le statistiche annuali con selettore anno; il selettore naviga correttamente a /admin?year=X.
Task 2: Elimina rotta /admin/analytics (CLEAN-03) - src/app/admin/analytics/page.tsx — conferma che non è più necessario (Task 1 ha già portato tutto il contenuto nella dashboard) src/app/admin/analytics/page.tsx 1. Verifica preventiva — assicurati che nessun altro file nel progetto importi da src/app/admin/analytics/page.tsx direttamente (è una page, non un modulo, quindi non dovrebbe avere consumer diretti — ma controlla): grep -r "admin/analytics" src/ --include="*.tsx" --include="*.ts" Se trovi riferimenti diversi da YearSelector (già aggiornato in Task 1), risolvili prima. 2. Elimina: - src/app/admin/analytics/page.tsx - Directory src/app/admin/analytics/ (se vuota dopo la rimozione del file) NON eliminare: lib/analytics-queries.ts (ancora importata dalla dashboard), src/components/admin/YearSelector.tsx (ancora usata dalla dashboard). test -f /Users/simonecavalli/Vault/IAMCAVALLI/src/app/admin/analytics/page.tsx && echo "ESISTE_ANCORA" || echo "OK_RIMOSSO" Risultato atteso: OK_RIMOSSO Verifica nessun riferimento rimasto: grep -r "admin/analytics" /Users/simonecavalli/Vault/IAMCAVALLI/src --include="*.tsx" --include="*.ts" | grep -v "node_modules" | grep -v ".next" Risultato atteso: 0 righe. - src/app/admin/analytics/page.tsx non esiste più - grep -r "admin/analytics" src/ produce 0 risultati - npm run build completa senza errori La rotta /admin/analytics è eliminata. Navigarci restituisce 404. Tutte le statistiche sono accessibili alla Dashboard /admin.
## Trust Boundaries | Boundary | Description | |----------|-------------| | admin/page.tsx → analytics-queries | Aggiunta di query server-side già esistenti — nessuna nuova superficie | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-18-03 | Information Disclosure | admin/page.tsx | accept | Le statistiche erano già accessibili a /admin/analytics — stessa autenticazione Auth.js, nessuna nuova esposizione | | T-18-04 | Tampering | YearSelector.tsx | accept | Il cambio da /admin/analytics a /admin nella navigazione è una semplice redirect di path — nessun dato mutato | Al termine del piano: 1. `npm run build` completa senza errori TypeScript 2. `grep -r "admin/analytics" src/` → 0 risultati 3. La Dashboard /admin mostra le sezioni Statistiche con selettore anno 4. Navigare a /admin/analytics restituisce 404 5. YearSelector porta a /admin?year=X quando si cambia anno - CLEAN-03: /admin mostra le statistiche annuali (MetricCard + MonthlyChart + time tracking) - /admin/analytics → 404 (rotta eliminata) - YearSelector naviga a /admin?year=X - Build TypeScript pulita After completion, create `.planning/phases/18-cleanup-consolidamento/18-02-SUMMARY.md`