# TerManager2

Applicazione web per la **gestione dell'assegnazione e rientro di territori** (cartoline), statistiche di percorrenza e campagne speciali per congregazioni.

---

## Indice

- [Panoramica](#panoramica)
- [Stack tecnologico](#stack-tecnologico)
- [Requisiti di sistema](#requisiti-di-sistema)
- [Installazione rapida](#installazione-rapida)
- [Configurazione](#configurazione)
- [Struttura del progetto](#struttura-del-progetto)
- [Funzionalità principali](#funzionalità-principali)
- [Ruoli e permessi (RBAC)](#ruoli-e-permessi-rbac)
- [Sicurezza e GDPR](#sicurezza-e-gdpr)
- [Modello dati](#modello-dati)
- [Regole di business](#regole-di-business)
- [Pagine dell'applicazione](#pagine-dellapplicazione)
- [Comandi utili](#comandi-utili)
- [Produzione](#produzione)
- [Licenza](#licenza)

---

## Panoramica

**TerManager2** gestisce:

- **Territori** (cartoline) con PDF allegato, stato dinamico (in reparto / assegnato / da rientrare / prioritario / inattivo), zona e tipologia.
- **Proclamatori** (assegnatari) con dati personali **cifrati a livello di colonna** nel database (AES-256-CBC tramite `APP_KEY`).
- **Assegnazioni** con data assegnazione/rientro, anno teocratico, conteggio opzionale in campagna.
- **Campagne** (periodi speciali) con percentuali di percorrenza in tempo reale.
- **Dashboard** con media mensile di percorrenza, campagna attiva, e tre liste rapide (da assegnare, prioritari, da rientrare).
- **Registro** delle assegnazioni filtrabile per anno teocratico, zona, stato.
- **Audit Log** completo di tutte le azioni utente con diff before/after.

---

## Stack tecnologico

| Componente       | Tecnologia                                    |
|------------------|-----------------------------------------------|
| **Framework**    | Laravel 11.x                                  |
| **PHP**          | 8.3-FPM                                       |
| **UI**           | Livewire 3.5 + TailwindCSS 4.0 + Alpine.js   |
| **Database**     | MariaDB 11                                    |
| **Cache/Session**| Redis 7 (Alpine)                              |
| **Web Server**   | Nginx 1.25 (Alpine)                           |
| **Auth**         | Laravel Breeze 2.0                            |
| **RBAC**         | spatie/laravel-permission 6.4                 |
| **Audit Trail**  | spatie/laravel-activitylog 4.8                |
| **Asset Build**  | Vite 6.0 + @tailwindcss/vite                 |
| **Node.js**      | 20 LTS (nel container PHP)                    |
| **Mail (dev)**   | Mailpit                                       |
| **Container**    | Docker Compose (5 servizi)                    |

---

## Requisiti di sistema

- **Docker** >= 24.0 e **Docker Compose** >= 2.20
- Oppure, senza Docker:
  - PHP >= 8.3 con estensioni: pdo_mysql, mbstring, gd, intl, zip, bcmath, redis, opcache
  - Composer >= 2.7
  - Node.js >= 20 LTS + npm
  - MariaDB >= 11 (o MySQL 8)
  - Redis >= 7

---

## Installazione

### Prerequisiti

- **Docker** e **Docker Compose** (v2) installati
- Porte libere: `8080` (app), `3306` (MariaDB), `6379` (Redis), `8025` (Mailpit)

### Procedura

```bash
# 1. Clona il repository
git clone <repository-url> termanager2
cd termanager2

# 2. Configura l'ambiente
cp .env.example .env

# 3. Imposta i permessi dei file (UID 1000 = appuser nel container)
chown -R 1000:1000 .
mkdir -p storage/app/public storage/framework/{cache/data,sessions,views} storage/logs bootstrap/cache
chmod -R 775 storage bootstrap/cache

# 4. Avvia i container (build al primo avvio)
docker compose up -d --build
```

Il codice applicativo e montato direttamente dal filesystem host, quindi ogni modifica locale e immediatamente visibile nel container (senza rebuild ad ogni edit).

Al primo avvio del container `app` vengono eseguiti automaticamente:

- setup `.env` e `APP_KEY`
- install/build dipendenze
- `php artisan migrate --force`
- `php artisan db:seed --force` **solo al primo avvio** (marker persistente)
- creazione admin iniziale se il database non ha utenti

> **Nota**: La `APP_KEY` viene generata automaticamente al primo avvio se assente nel `.env`.

Al termine l'applicazione sarà disponibile su: **http://localhost:8080**

### Reset completo

Per ripartire da zero (elimina database e volumi):

```bash
docker compose down -v --remove-orphans
docker compose up -d --build
# Ripetere i passaggi dal punto 4 in poi
```

---

## Configurazione

### Variabili d'ambiente principali (`.env`)

| Variabile             | Default              | Descrizione                              |
|-----------------------|----------------------|------------------------------------------|
| `APP_KEY`             | (generata)           | Chiave AES-256 per cifratura. **Mai condividere** |
| `APP_PORT`            | `8080`               | Porta host per l'applicazione            |
| `SEED_DEV_DATA`       | `false`              | Se `true`, `php artisan db:seed` include anche i dati demo |
| `RUN_DB_SEED_ON_FIRST_START` | `true`        | Se `true`, esegue il seed automatico solo al primo avvio container |
| `ENSURE_INITIAL_ADMIN_ON_EMPTY_DB` | `true`  | Se `true`, richiede/crea admin iniziale quando non esistono utenti |
| `INITIAL_ADMIN_NAME`  | vuoto                | Nome admin creato al primo avvio         |
| `INITIAL_ADMIN_EMAIL` | vuoto                | Email admin creata al primo avvio        |
| `INITIAL_ADMIN_PASSWORD` | vuoto             | Password admin creata al primo avvio (min 8) |
| `DB_DATABASE`         | `termanager2`        | Nome database MariaDB                    |
| `DB_USERNAME`         | `termanager2`        | Utente database                          |
| `DB_PASSWORD`         | `secret`             | Password database                        |
| `DB_ROOT_PASSWORD`    | `rootsecret`         | Password root MariaDB                    |
| `REDIS_PASSWORD`      | `redissecret`        | Password Redis                           |
| `MAIL_PORT`           | `1025`               | Porta SMTP Mailpit                       |
| `MAILPIT_UI_PORT`     | `8025`               | UI Mailpit per debug email               |
| `USER_ID` / `GROUP_ID`| `1000`              | UID/GID container (match con host)       |

### Configurazione iniziale

La configurazione viene gestita dalla sezione **Impostazioni** (menu amministrazione), senza wizard iniziale.

### Credenziali iniziali

Le credenziali non sono hardcoded nel progetto. Al primo avvio devi impostare in `.env`:

- `INITIAL_ADMIN_NAME`
- `INITIAL_ADMIN_EMAIL`
- `INITIAL_ADMIN_PASSWORD`

Se il database e vuoto e queste variabili non sono valorizzate, il container `app` interrompe l'avvio con errore esplicito.

---

## Struttura del progetto

```
TerManager2/
├── app/
│   ├── Console/Commands/          # AuditCleanup (pulizia log schedulata)
│   ├── Http/Middleware/           # Middleware HTTP applicativi
│   ├── Livewire/
│   │   ├── Assegnazioni/         # Assegna, Rientra
│   │   ├── Auth/                 # Login
│   │   ├── Campagne/             # Index, Create, Edit, Show
│   │   ├── Proclamatori/         # Index, Create, Edit, Show, Cestino
│   │   ├── Settings/             # SettingsEdit, ZoneIndex, TipologieIndex
│   │   ├── Territori/            # Index, Create, Edit, Show, Cestino
│   │   ├── AuditLog.php          # Log attività (filtri, diff)
│   │   ├── Home.php              # Dashboard
│   │   └── Registro.php          # Registro assegnazioni
│   ├── Models/                   # 8 modelli Eloquent
│   └── Providers/                # AppServiceProvider (Gate admin)
├── bootstrap/                    # app.php (middleware), providers.php
├── config/                       # app, auth, database, session
├── database/
│   ├── migrations/               # 10 migrazioni ordinate
│   └── seeders/                  # Roles, Dev, Database seeders
├── docker/
│   ├── nginx/default.conf        # Vhost con security headers
│   └── php/
│       ├── Dockerfile            # PHP 8.3-FPM + estensioni + Composer + Node 20
│       └── php.ini               # 64M upload, opcache, Europe/Rome
├── public/index.php              # Entry point Laravel
├── resources/
│   ├── css/app.css               # TailwindCSS 4 import
│   ├── js/                       # app.js, bootstrap.js (Axios)
│   └── views/
│       ├── components/layouts/   # app.blade.php (sidebar), guest.blade.php
│       └── livewire/             # Tutte le viste dei componenti
├── routes/
│   ├── web.php                   # Tutte le rotte con permessi
│   └── console.php               # Schedule audit:cleanup daily
├── .env.example                  # Template configurazione
├── artisan                       # CLI Laravel
├── composer.json                 # Dipendenze PHP
├── docker-compose.yml            # 5 servizi (app, nginx, mariadb, redis, mailpit)
├── package.json                  # Dipendenze Node (Vite, TailwindCSS)
└── vite.config.js                # Vite + Laravel plugin + TailwindCSS plugin
```

---

## Funzionalità principali

### Dashboard (Home)
- Anno teocratico corrente (auto-creato se mancante, Set→Ago)
- Media mensile di percorrenza (territori rientrati / mesi trascorsi)
- Campagna attiva con barra di progresso percentuale
- Tre liste rapide con azione diretta:
  - **Da assegnare**: territori in reparto, ordinati per priorità
  - **Prioritari**: flag manuale O soglia automatica (la soglia vince sempre)
  - **Da rientrare**: assegnazioni oltre la soglia giorni configurata

### Territori
- CRUD completo con upload/sostituzione PDF
- Filtri per zona, tipologia, stato (in_reparto, assegnato, da_rientrare, inattivo)
- Badge di stato colorati + indicatore priorità (Manuale/Automatico)
- Dettaglio con PDF viewer inline e storico assegnazioni per anno teocratico
- Toggle attivo/inattivo e prioritario
- Soft delete con cestino (ripristino o eliminazione definitiva)

### Proclamatori
- CRUD con campi nome/cognome **cifrati nel DB** (AES-256-CBC)
- Ricerca in-memory (necessaria per campi cifrati, ottimizzata per < 200 record)
- Conteggio territori attualmente assegnati
- Toggle attivo/inattivo
- **Anonimizzazione GDPR** (sostituzione dati con placeholder irreversibile)
- Soft delete con cestino

### Assegnazione / Rientro
- Selezione territorio disponibile + proclamatore attivo
- Data assegnazione con auto-detection anno teocratico
- Validazione: territorio non già assegnato, entrambi attivi
- Rientro con calcolo automatico giorni
- **Prompt retroattivo campagna**: se l'assegnazione ricade nel range di una campagna, viene chiesto se conteggiare il rientro

### Campagne
- CRUD con date inizio/fine e descrizione
- Stato automatico (Attiva/Futura/Conclusa)
- Percentuale percorrenza in tempo reale (conteggiati / totali assegnati nel range)
- Dettaglio con lista territorio-per-territorio dei conteggiati

### Registro Assegnazioni
- Filtri: anno teocratico, zona, stato (aperte/chiuse), ricerca testo
- Colonne: territorio, proclamatore, date, giorni, campagna

### Audit Log
- Log automatico su tutti i modelli (spatie/laravel-activitylog)
- Log manuale per eventi di flusso (assign, return, login, logout)
- Filtri per utente, tipo evento, ricerca testo
- Dettaglio con diff before/after (properties JSON espanso)
- Pulizia automatica schedulata (`audit:cleanup` — retention configurabile)

### Impostazioni
- Nome congregazione
- Soglia mesi priorità automatica
- Soglia giorni per "da rientrare"
- Retention giorni audit log

### XML Exchange
- Conversione dump SQL legacy in XML compatibile con TerManager2
- Import XML nell'app (sostituzione dati gestionali: impostazioni, zone, tipologie, proclamatori, territori, anni, campagne, assegnazioni)
- Export XML dei dati correnti

### Zone e Tipologie
- CRUD inline (aggiungi, rinomina, attiva/disattiva, elimina)
- Protezione: non eliminabile se ha territori associati

---

## Ruoli e permessi (RBAC)

Tre ruoli con permessi granulari gestiti da **spatie/laravel-permission**:

| Permesso            | Amministratore | Assistente | Operatore |
|---------------------|:--------------:|:----------:|:---------:|
| `settings.manage`   | ✅             |            |           |
| `territori.manage`  | ✅             |            |           |
| `proclamatori.manage`| ✅            | ✅         |           |
| `campagne.manage`   | ✅             | ✅         |           |
| `territori.assign`  | ✅             | ✅         | ✅        |
| `territori.return`  | ✅             | ✅         | ✅        |
| `registro.view`     | ✅             | ✅         |           |
| `registro.export`   | ✅             |            |           |
| `audit.view`        | ✅             |            |           |
| `audit.export`      | ✅             |            |           |

L'amministratore ha un bypass completo via `Gate::before` nel `AppServiceProvider`.

Il menu sidebar mostra solo le voci per cui l'utente ha permesso.

---

## Sicurezza e GDPR

### Sicurezza
- **Cifratura a riposo**: nome e cognome proclamatori cifrati con `encrypted` cast (AES-256-CBC via `APP_KEY`)
- **Password**: hash bcrypt (standard Laravel)
- **RBAC**: middleware `permission:` su ogni rotta
- **CSRF**: token integrato in tutti i form Livewire
- **Rate limiting**: login con throttle configurabile
- **Security headers**: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection (via Nginx)
- **Sessioni**: Redis con cookie `termanager2_session` (httpOnly, secure in produzione)

### GDPR
- **Minimizzazione**: solo nome, cognome e stato attivo per i proclamatori
- **Cifratura a riposo**: dati illeggibili con accesso diretto al DB
- **Audit trail**: log completo di tutte le operazioni sui dati
- **Diritto all'oblio**: metodo `anonimizza()` sul Proclamatore (sostituisce dati con placeholder)
- **Retention**: pulizia automatica dei log audit oltre il periodo configurato
- **Ruoli e autorizzazioni**: accesso ai dati solo per chi ha permessi specifici
- **No log di dati sensibili**: l'audit log registra `proclamatore_id` ma non nome/cognome in chiaro nei `properties`

---

## Modello dati

### Tabelle principali (10 migrazioni)

| Tabella           | Descrizione                                                  |
|-------------------|--------------------------------------------------------------|
| `users`           | Utenti app con auth standard Laravel                         |
| `settings`        | Configurazione singleton (congregazione, soglie, retention)  |
| `zone`            | Zone territoriali (id, nome, attivo)                         |
| `tipologie`       | Tipologie territorio (id, nome, attivo)                      |
| `proclamatori`    | Assegnatari con nome/cognome cifrati, soft delete            |
| `territori`       | Cartoline con zona, tipologia, PDF, soft delete              |
| `anni_teocratici` | Periodi Set→Ago con label univoca                            |
| `campagne`        | Periodi speciali con date e descrizione                      |
| `assegnazioni`    | Join territorio↔proclamatore con date, anno, campagna        |
| `activity_log`    | Audit trail (spatie/laravel-activitylog)                     |

### Indici e vincoli
- `territori.numero` — UNIQUE
- `anni_teocratici.label` — UNIQUE
- `assegnazioni(territorio_id, returned_at)` — INDEX composito
- `assegnazioni(proclamatore_id, returned_at)` — INDEX composito
- `assegnazioni(anno_teocratico_id)` — INDEX
- FK con `RESTRICT` su territorio e proclamatore (protezione integrità)

---

## Regole di business

### Anno teocratico
- **Settembre → Agosto**: se mese ≥ 9 → anno = `corrente/corrente+1`; se mese ≤ 8 → anno = `corrente-1/corrente`
- Creato automaticamente se non esiste (metodo `AnnoTeocratico::perData()`)
- L'assegnazione conta per l'anno determinato dalla **data di assegnazione**

### Stato territorio (calcolato dinamicamente)
- **in_reparto**: attivo, nessuna assegnazione aperta
- **assegnato**: ha assegnazione con `returned_at = null`
- **da_rientrare**: assegnato da più giorni della soglia configurata
- **inattivo**: `attivo = false`

### Priorità
- **Manuale**: flag `prioritario = true` impostato dall'admin
- **Automatica**: nessuna assegnazione negli ultimi N mesi (soglia configurabile)
- La soglia automatica **vince sempre** sul flag manuale (l'admin può aggiungere ma non rimuovere se la soglia scatta)

### Campagne — percentuale percorrenza
- **Denominatore**: tutti i territori assegnati nel range della campagna
- **Numeratore**: assegnazioni conteggiate in campagna (`counted_in_campaign = true`)
- Il prompt retroattivo appare al rientro se l'assegnazione è nel range di una campagna

---

## Comandi utili

```bash
# Avviare i container
docker compose up -d

# Fermare i container
docker compose down

# Shell nel container app
docker compose exec app bash

# Eseguire migrazioni
docker compose exec app php artisan migrate

# Seed dati di sviluppo
docker compose exec app php artisan db:seed

# Compilare asset (dev con hot reload)
docker compose exec app npm run dev

# Compilare asset (produzione)
docker compose exec app npm run build

# Pulizia manuale audit log
docker compose exec app php artisan audit:cleanup

# Cache configurazione (produzione)
docker compose exec app php artisan config:cache
docker compose exec app php artisan route:cache
docker compose exec app php artisan view:cache

# Svuotare cache
docker compose exec app php artisan optimize:clear
```

---

## Produzione

Per il deploy in produzione:

1. **Variabili d'ambiente**:
   - `APP_ENV=production`
   - `APP_DEBUG=false`
   - Password sicure per DB e Redis (non i default)
   - `APP_KEY` generata e conservata come secret

2. **HTTPS**: configurare un reverse proxy (Traefik, Nginx) con certificato SSL/TLS e HSTS

3. **Immagini Docker**: build senza volume codice montato, asset pre-compilati

4. **Backup**: backup cifrati del database con rotazione automatica

5. **Segreti**: gestire `APP_KEY`, password DB/Redis con Docker secrets o secret manager

6. **Performance**:
   - `php artisan config:cache && route:cache && view:cache`
   - OPcache abilitato (già configurato in `php.ini`)
   - Redis per cache, sessioni e code

7. **Monitoraggio**: configurare health checks e log aggregation

---

## Licenza

Progetto privato — tutti i diritti riservati.
