# Task Tracker — Spécification fonctionnelle et technique v1

> Micro-app de gestion des tâches employés, par département, avec rôles admin / manager / staff.
> Objectif : savoir qui fait quoi, vérifier que c'est fait, et absorber les tâches récurrentes et les départs sans perte d'historique.

---

## 1. Rôles et permissions

Trois rôles. L'admin est un manager transverse avec la gestion des départements et des comptes en plus — pas de logique dupliquée.

| Action | Staff | Manager (son dépt) | Admin |
|---|---|---|---|
| Voir ses tâches, les prendre dans le pool, marquer « faite » | ✓ | ✓ | ✓ |
| Créer, assigner, réassigner des tâches | – | ✓ | ✓ |
| Créer / modifier des tâches récurrentes | – | ✓ | ✓ |
| Valider ou renvoyer (« à refaire ») une tâche faite | – | ✓ | ✓ |
| Voir toutes les tâches de son département | – | ✓ | ✓ |
| Voir tous les départements | – | – | ✓ |
| Ajouter / modifier / archiver des **staffs** | – | ✓ | ✓ |
| Réassigner en masse les tâches d'un partant | – | ✓ | ✓ |
| Créer des comptes **manager** ou **admin**, changer un rôle | – | – | ✓ |
| Transférer un staff vers un autre département | – | – | ✓ |
| Créer / renommer / archiver des départements | – | – | ✓ |

Règles transverses :

- Un **manager est rattaché à un département** et ne voit / ne gère que celui-ci. L'admin est transverse (`department_id` nullable).
- Toute autorisation est vérifiée **côté serveur** dans chaque Server Action (`requireRole` + `assertSameDepartment`). L'UI ne fait que masquer, jamais protéger.
- Un manager ne peut pas piocher dans l'équipe d'un autre : le transfert inter-départements est réservé à l'admin.

## 2. Cycle de vie d'une tâche

Statuts (enum `task_status`, valeurs en anglais dans le code, libellés français en UI) :

| Code | UI | Posé par |
|---|---|---|
| `todo` | À faire | Création, renvoi « à refaire », réassignation |
| `in_progress` | En cours | Le staff assigné (optionnel) |
| `done` | Faite | Le staff assigné (+ note de complétion optionnelle) |
| `validated` | Validée | Manager / admin après vérification |
| `missed` | Manquée | Le cron, pour les instances récurrentes dont l'échéance est dépassée |

Transitions autorisées :

```
todo ──► in_progress ──► done ──► validated   (fin de vie, historique conservé)
  ▲                        │
  └────── « à refaire » ◄──┘   (manager, avec note de renvoi optionnelle)

todo / in_progress ──► missed   (cron uniquement, instances récurrentes échues)
```

- « En retard » n'est **pas** un statut : c'est un état calculé (`due_date < aujourd'hui` et statut `todo`/`in_progress`), affiché en badge.
- `missed` est réservé aux instances récurrentes : une tâche journalière non faite hier ne s'empile pas, elle est marquée manquée. C'est ce qui rend le reporting de conformité honnête (« le nettoyage a-t-il été fait chaque jour ce mois-ci ? »).
- Une tâche `validated` ou `missed` ne bouge plus (sauf correction admin en base, hors périmètre UI v1).

## 3. Tâches récurrentes

Principe : **le modèle est séparé de l'occurrence.** `recurring_tasks` décrit la règle ; le cron génère chaque nuit des instances dans `tasks`. Une tâche récurrente n'est jamais « réinitialisée » — chaque jour/semaine/mois produit une nouvelle ligne, donc un historique complet.

Fréquences v1 :

| `frequency` | Champ complémentaire | Génération |
|---|---|---|
| `daily` | – | Tous les jours |
| `weekly` | `day_of_week` (1 = lundi … 7 = dimanche, ISO) | Le jour correspondant |
| `monthly` | `day_of_month` (1–31, borné au dernier jour du mois : 31 → 28/29/30 si besoin) | Le jour correspondant |

Règles :

- L'assignation d'un modèle est **une personne OU le pool du département** (`assignee_id = null` + `department_id` obligatoire). Une instance de pool est visible par tout le département ; un staff la « prend » (claim) et devient assigné.
- Le job nocturne (cron, `05:00 → 00:05` Europe/Paris, cf. §8) fait deux choses dans l'ordre : (1) marque `missed` les instances récurrentes échues encore ouvertes, (2) génère les instances du jour.
- La génération est **idempotente** : index unique `(recurring_id, due_date)` + `onConflictDoNothing`. Relancer le job ne duplique rien.
- Désactiver un modèle (`active = false`) stoppe la génération sans toucher aux instances passées.

## 4. Départements

- Table `departments`, gérée par l'admin uniquement.
- **Renommer** est toujours sans risque (tout est référencé par id).
- **Supprimer n'existe pas** : on archive (`archived_at`). L'archivage est refusé tant que le département contient des staffs actifs — l'admin doit d'abord les transférer. Zéro tâche orpheline possible.
- Chaque user (sauf admin) et chaque tâche / modèle récurrent est rattaché à un département.

## 5. Gestion des staffs et départs

**On n'efface jamais un compte, on l'archive** (`archived_at`). Un compte archivé ne peut plus se connecter, disparaît des listes d'assignation, mais son profil, sa `job_description` et tout son historique restent consultables.

Flow de départ (écran « Départ d'un staff ») :

1. Le manager (ou l'admin) déclenche l'archivage.
2. L'app affiche le **bilan** : ses instances ouvertes (`todo`, `in_progress`, `missed`) et ses modèles récurrents.
3. Réassignation **en masse ou ligne par ligne**, vers un autre staff actif du département ou vers le pool (`assignee_id = null`).
4. Le tout est exécuté dans **une transaction** (archivage + 2 UPDATE). Les tâches `done`/`validated` ne bougent pas : elles restent à son nom.
5. La `job_description` du partant reste lisible sur le profil archivé — copiable/modifiable sur le remplaçant.

Autres règles :

- Un manager crée uniquement des comptes **staff** de son département. Comptes manager/admin et changements de rôle : admin.
- **Transfert de département** (admin) : ses instances ouvertes et ses modèles récurrents retombent dans le **pool du département d'origine** (les tâches appartiennent au département, pas à la personne).

## 6. Modèle de données

4 tables. Schéma Drizzle complet dans `src/db/schema.ts` (source de vérité).

```
departments      id · name · archived_at · created_at

users            id · name · email(unique) · role(admin|manager|staff)
                 department_id → departments (null = admin transverse)
                 job_description · archived_at · created_at

recurring_tasks  id · title · description · frequency(daily|weekly|monthly)
                 day_of_week · day_of_month
                 department_id → departments · assignee_id → users (null = pool)
                 active · created_by → users · created_at

tasks            id · title · description · status(todo|in_progress|done|validated|missed)
                 department_id → departments · assignee_id → users (null = pool)
                 recurring_id → recurring_tasks (null = ponctuelle) · due_date
                 completion_note (preuve du staff) · review_note (renvoi manager)
                 created_by · completed_at · validated_at · validated_by
                 created_at · updated_at
                 UNIQUE (recurring_id, due_date)
```

## 7. Écrans v1

| Route | Rôle | Contenu |
|---|---|---|
| `/mes-taches` | tous | Ses tâches du jour + pool de son département (bouton « prendre »), badge retard, marquer en cours / faite (+ note) |
| `/equipe` | manager, admin | Dashboard département : compteurs par personne (à faire, en retard, faites, manquées, validées), **charge de travail** (nb de tâches ouvertes par staff pour repérer les déséquilibres), taux de complétion, filtres statut/personne, validation & renvoi en un clic |
| `/equipe/recurrentes` | manager, admin | CRUD des modèles récurrents (fréquence, assigné ou pool, actif/inactif) |
| `/equipe/depart/[userId]` | manager, admin | Écran de départ : bilan + réassignation en masse |
| `/admin/utilisateurs` | admin | Tous les comptes, rôles, transferts de département, archivés inclus |
| `/admin/departements` | admin | CRUD départements (archivage protégé) |

L'admin voit `/equipe` avec un sélecteur de département.

## 8. Stack et architecture

- **Next.js (App Router)** + **Server Actions** pour toutes les mutations. Pas d'API REST séparée en v1 ; si un consommateur externe arrive (mobile, intégration), des route handlers `/api` appelleront la même logique.
- **Drizzle ORM + PostgreSQL** (TypeScript strict). Migrations via `drizzle-kit`.
- **Better Auth** : session avec `role` et `department_id`. `middleware.ts` protège les routes ; chaque action revérifie (`src/lib/auth.ts`).
- **Cron** : `src/jobs/generate-recurring.ts`, lancé chaque nuit à 00:05 (crontab / node-cron / BullMQ au choix de l'infra). Timezone de référence : Europe/Paris.
- **Notifications v1** (à brancher, provider email au choix — Resend/SMTP) : email à l'assignation d'une tâche ponctuelle + digest quotidien par staff (tâches du jour, retards). Rien de plus en v1.

## 9. Hors périmètre v1 / évolutions prévues

| Évolution | Déclencheur | Impact |
|---|---|---|
| Table `positions` (postes) : job description et modèles récurrents attachés au **poste**, pas à la personne | Turnover fréquent | Déplacer 2 colonnes — un départ = changer le titulaire, zéro réassignation |
| `task_events` (journal d'audit : qui a fait/validé/réassigné quoi, quand) | Besoin de traçabilité fine | Table additionnelle, aucun impact sur l'existant |
| Route handlers `/api` | App mobile / intégrations | Réutilise la logique des actions |
| Pièces jointes photo sur `completion_note` | Preuve visuelle exigée | Stockage fichiers à ajouter |

**Volontairement exclus** (cimetière des outils de tâches) : sous-tâches, projets, tags, priorités multiples. Le besoin exprimé est couvert sans.

## 10. Critères d'acceptation clés

1. Aucune mutation possible hors de son scope (testé côté serveur, pas seulement UI).
2. Relancer le cron deux fois le même jour ne crée aucun doublon.
3. Une tâche journalière non faite hier est `missed` ce matin, et une nouvelle instance du jour existe.
4. Archiver un staff avec 12 tâches ouvertes et 3 modèles récurrents : tout est réassigné en une opération, son historique `done`/`validated` est intact et consultable.
5. Impossible d'archiver un département contenant un staff actif.
6. Un staff archivé ne peut plus se connecter mais apparaît toujours dans l'historique des tâches validées.
