---
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"
---

<objective>
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.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
</context>

<interfaces>
<!-- Estratto da src/app/admin/analytics/page.tsx — contratti da portare nella dashboard -->

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.
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Integra statistiche annuali nella Dashboard (CLEAN-03)</name>

  <read_first>
    - 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)
  </read_first>

  <files>
    src/app/admin/page.tsx,
    src/components/admin/YearSelector.tsx
  </files>

  <action>
    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 ── */}
       <div className="mt-10 space-y-10">
         <div className="flex items-center justify-between">
           <div>
             <h2 className="text-xl font-bold text-gray-900">Statistiche</h2>
             <p className="text-sm text-gray-400 mt-0.5">Panoramica per anno</p>
           </div>
           <YearSelector currentYear={year} availableYears={availableYears} />
         </div>

         {/* Sezione economica */}
         <div className="space-y-4">
           <h3 className="text-sm font-bold text-[#71717a] uppercase tracking-wider">Fatturato</h3>
           <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
             <MetricCard label="Contrattualizzato" value={fmtEur(data.contracted)}
               sub={`${data.clientsAcquired} client${data.clientsAcquired === 1 ? "e" : "i"}`} accent />
             <MetricCard label="Incassato" value={fmtEur(data.collected)}
               sub={`${collectedPct}% del contrattualizzato`} />
             <MetricCard label="Da incassare" value={fmtEur(data.pending)} sub="Tutti gli anni" />
             <MetricCard label="Clienti acquisiti" value={String(data.clientsAcquired)} sub={`Anno ${year}`} />
           </div>
           <MonthlyChart data={monthly} year={year} />
         </div>

         {/* Sezione time tracking */}
         <div className="space-y-4">
           <h3 className="text-sm font-bold text-[#71717a] uppercase tracking-wider">Tempo tracciato</h3>
           <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
             <MetricCard label="Ore totali" value={`${totalHours}h`} sub={`Anno ${year}`} accent />
           </div>
           {/* ... tabella ore per cliente (copia da analytics/page.tsx) ... */}
         </div>
       </div>
       ```
       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).
  </action>

  <verify>
    <automated>
      grep "getAnalyticsByYear\|getMonthlyCollected\|getAvailableYears" /Users/simonecavalli/Vault/IAMCAVALLI/src/app/admin/page.tsx | wc -l
    </automated>
    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".
  </verify>

  <acceptance_criteria>
    - 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
  </acceptance_criteria>

  <done>
    La Dashboard mostra le statistiche annuali con selettore anno; il selettore naviga
    correttamente a /admin?year=X.
  </done>
</task>

<task type="auto">
  <name>Task 2: Elimina rotta /admin/analytics (CLEAN-03)</name>

  <read_first>
    - src/app/admin/analytics/page.tsx — conferma che non è più necessario (Task 1 ha già portato
      tutto il contenuto nella dashboard)
  </read_first>

  <files>
    src/app/admin/analytics/page.tsx
  </files>

  <action>
    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).
  </action>

  <verify>
    <automated>
      test -f /Users/simonecavalli/Vault/IAMCAVALLI/src/app/admin/analytics/page.tsx && echo "ESISTE_ANCORA" || echo "OK_RIMOSSO"
    </automated>
    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.
  </verify>

  <acceptance_criteria>
    - src/app/admin/analytics/page.tsx non esiste più
    - grep -r "admin/analytics" src/ produce 0 risultati
    - npm run build completa senza errori
  </acceptance_criteria>

  <done>
    La rotta /admin/analytics è eliminata. Navigarci restituisce 404.
    Tutte le statistiche sono accessibili alla Dashboard /admin.
  </done>
</task>

</tasks>

<threat_model>
## 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 |
</threat_model>

<verification>
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
</verification>

<success_criteria>
- 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
</success_criteria>

<output>
After completion, create `.planning/phases/18-cleanup-consolidamento/18-02-SUMMARY.md`
</output>
