Files
Presencia/README.md
Claude 3df521ac30 Revue complète : sécurité, optimisations et UI/UX mobile
Sécurité
- JWT : un JWT_SECRET d'exemple ou trop court est ignoré au profit d'un
  secret aléatoire conservé en base (avant, la valeur publique de
  docker-compose.yml permettait de forger un jeton admin).
- Le compte est relu en base à chaque requête : désactivation, changement
  de rôle et réinitialisation du mot de passe prennent effet immédiatement.
- Connexion : 10 échecs max par e-mail / 15 min, temps constant que l'e-mail
  existe ou non, mot de passe admin retiré des logs.
- Validation des entrées (ids, dates, mois, longueurs, e-mail) : 400 au
  lieu de 500. Un admin ne peut plus se désactiver ni se rétrograder.
- Mots de passe : 8 caractères minimum ; changement en libre-service.
- Export : nom de fichier conforme RFC 5987 (un nom de société avec « — »
  faisait planter l'export).
- esc() échappe aussi les guillemets (injection d'attributs HTML).
- En-têtes CSP / X-Frame-Options / nosniff (Nginx + API), API et Postgres
  publiés sur 127.0.0.1 seulement, image backend non-root via npm ci.

Optimisations
- Export : une requête pour tout le mois au lieu d'une par cadre.
- Saisie groupée : un seul INSERT (unnest), doublons dédupliqués.
- Dates renvoyées en chaînes (plus de décalage d'un jour selon le TZ).
- Planning : chaque clic met à jour l'affichage localement au lieu de
  recharger le mois ; les réponses de mois périmées sont ignorées.
- Recherche Utilisateurs / Plannings filtrée localement (plus une requête
  par touche) ; statuts des sociétés chargés en parallèle.

UI/UX
- Mobile : toutes les destinations dans la barre basse (Sociétés et Ma
  saisie étaient inaccessibles à l'admin) ; tableaux affichés en cartes
  (les boutons d'action étaient hors écran).
- Boutons désactivés pendant l'envoi, focus et Échap dans les dialogues,
  retour à l'écran de connexion quand la session est révoquée, erreurs
  d'export affichées au lieu d'un fichier JSON téléchargé, actions
  destructives signalées, confirmation avant désactivation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D5Bdayziw6tybgqETZoNSt
2026-10-09 07:17:54 +00:00

119 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Presencia
Application de gestion des présences par demi-journée, multi-sociétés, avec validation mensuelle en deux étapes (cadre puis administrateur) et export PDF/Excel par société.
## Fonctionnalités
- **Connexion uniquement** : aucune inscription publique. Tous les comptes (admin et cadres) sont créés par un administrateur.
- **Multi-sociétés** : chaque cadre est rattaché à une société. L'administrateur voit toutes les sociétés et tous les plannings ; un cadre ne voit que le sien.
- **Tableau de bord** adapté au rôle : compteurs du mois et raccourcis « à faire maintenant » (sociétés prêtes à valider, cadres en retard, saisie à compléter).
- **Planning mensuel** : clic sur une demi-journée (matin/après-midi) pour choisir Présent, Non-présent, Congé ou RTT. Trois affichages au choix (grille, compact, liste), **saisie rapide au pinceau** (on active un statut puis on peint les cases), **raccourcis clavier** dans le sélecteur (1–4 pour les statuts, ⌫ pour effacer, ↵ pour appliquer à la journée entière), et deux actions groupées : « Remplir les jours ouvrés » et « Tout effacer ».
- **Validation de fin de mois en deux temps** :
1. Chaque cadre **valide son propre mois** (verrouille ses saisies).
2. Une fois **tous les cadres d'une société** validés, l'administrateur **valide la société** pour ce mois (verrouillage complet). L'administrateur peut réouvrir un mois cadre ou une société en cas de correction nécessaire.
- **Historique** : chaque cadre retrouve ses mois précédents avec les totaux par statut.
- **Export** PDF et Excel par société et par mois (détail par cadre, jour et demi-journée).
- **Interface mobile** : sous 900 px, le calendrier devient une liste verticale, la navigation passe en barre basse et les cibles tactiles font 44 px.
- **Aucune dépendance externe au chargement** : la police (Inter) et les icônes (Phosphor) sont servies par l'application elle-même — aucune requête vers un CDN ou vers Google Fonts.
- **Docker** : Postgres + API Node/Express + frontend Nginx, sur des ports non standards.
## Démarrage
Toute la configuration (ports, mot de passe base de données, `JWT_SECRET`, identifiants admin) est définie directement dans `docker-compose.yml` — il n'y a pas de fichier `.env` à créer. Éditez les valeurs dans `docker-compose.yml` avant le premier démarrage (au minimum `POSTGRES_PASSWORD` et `ADMIN_PASSWORD`), puis :
```bash
docker compose up -d --build
```
Ports par défaut (modifiables directement dans `docker-compose.yml`) :
| Service | Port hôte | Accessible depuis |
|-----------|-----------|--------------------------|
| Frontend | 8781 | le réseau |
| API | 4790 | la machine hôte seulement |
| Postgres | 6543 | la machine hôte seulement |
Le navigateur passe toujours par le frontend (qui proxifie `/api`) ; l'API et
Postgres ne sont publiés que sur `127.0.0.1`, pour l'administration locale
(psql, sauvegardes).
**`JWT_SECRET`** : laissez-le vide pour que l'API génère un secret aléatoire,
conservé en base (les sessions survivent aux redémarrages). Si vous le
renseignez, il doit faire au moins 32 caractères aléatoires ; une valeur
d'exemple ou trop courte est ignorée.
Ouvrir http://localhost:8781
Un compte administrateur est créé automatiquement au premier démarrage avec les identifiants définis par `ADMIN_EMAIL` / `ADMIN_PASSWORD` dans `docker-compose.yml` (par défaut `admin@presencia.local` / `ChangeMe123!`). **Changez ce mot de passe après la première connexion** via « Mon mot de passe » (en bas de la barre latérale, ou l'icône cadenas sur mobile). Chaque utilisateur peut changer le sien ; un administrateur peut aussi réinitialiser celui de n'importe quel compte depuis l'onglet Utilisateurs.
Si vous changez les identifiants admin dans `docker-compose.yml` *après* un premier démarrage, ils n'auront aucun effet : le compte admin n'est créé qu'une seule fois (au premier démarrage, base vide). Pour le modifier ensuite, utilisez l'écran Utilisateurs une fois connecté, ou réinitialisez le volume `presencia_pgdata`.
## Utilisation
### En tant qu'administrateur
- **Sociétés** : créer / renommer / supprimer les sociétés.
- **Utilisateurs** : créer des comptes cadre ou admin, attribuer une société à un cadre, réinitialiser un mot de passe, activer/désactiver un compte.
- **Plannings** : consulter (et corriger si besoin) le planning de n'importe quel cadre.
- **Validation & export** : pour une société et un mois donnés, suivre la validation de chaque cadre, valider la société une fois tous les cadres validés, réouvrir si besoin, télécharger le PDF ou l'Excel récapitulatif.
### En tant que cadre
- Renseigner sa présence par demi-journée sur le mois en cours (ou les mois précédents/suivants). Le plus rapide : cliquer **« Remplir les jours ouvrés »** puis ne corriger que les exceptions (congés, RTT, absences).
- Pour saisir plusieurs cases d'affilée, activer un statut dans **Saisie rapide** : chaque clic applique directement ce statut, sans passer par le sélecteur.
- Dans le sélecteur d'une demi-journée : touches **1** à **4** pour les statuts, **⌫** pour effacer, **↵** pour appliquer à la journée entière.
- En fin de mois, cliquer sur **« Valider mon mois »** : les saisies sont alors verrouillées et transmises pour validation à l'administrateur. Si une correction est nécessaire après coup, il faut qu'un administrateur réouvre le mois.
- **Historique** : retrouver les mois précédents et leurs totaux, et les rouvrir en lecture.
## Sécurité
- Mots de passe hachés (bcrypt), 8 caractères minimum.
- Session par cookie `httpOnly` / `SameSite=Lax` (12 h). Passez `COOKIE_SECURE: "true"` si l'application est servie en HTTPS.
- Le compte est relu en base à chaque requête : désactiver un compte, changer son rôle ou réinitialiser son mot de passe prend effet immédiatement, sans attendre l'expiration de la session.
- Connexion limitée à 10 échecs par adresse e-mail sur 15 minutes.
- Un administrateur ne peut ni désactiver ni rétrograder son propre compte.
- En-têtes de sécurité (CSP, `X-Frame-Options`, `nosniff`…) posés par Nginx et par l'API.
## Architecture technique
```
backend/ API Node.js / Express, PostgreSQL (pg), auth par cookie JWT httpOnly
frontend/ Page HTML unique (vanilla JS), servie par Nginx qui proxifie /api vers le backend
public/css/styles.css tokens et composants du design system (Nocturne)
public/css/presencia.css couche applicative du design (calendrier, navigation, statuts)
public/css/app.css glue : mises en page que la maquette exprimait en styles inline
public/css/inter.css police Inter, servie localement
public/css/phosphor.css sous-ensemble des 18 icônes utilisées, servi localement
docker-compose.yml
```
`styles.css` et `presencia.css` proviennent du projet Claude Design et sont à
remplacer tels quels lors d'une nouvelle exportation du design ; les
adaptations propres à l'application vivent dans `app.css` pour que cette
réimportation reste triviale. Seule modification apportée à `styles.css` :
l'`@import` vers Google Fonts a été retiré au profit de `inter.css` servi
localement.
### Modèle de données (PostgreSQL)
- `companies` — sociétés
- `users` — comptes (rôle `admin` ou `cadre`, société associée pour les cadres)
- `attendance_entries` — une ligne par utilisateur / date / demi-journée (AM ou PM) / statut
- `month_locks` — validation du mois par le cadre (`cadre_validated`)
- `company_month_validations` — validation du mois par l'administrateur pour une société entière (`admin_validated`)
Règles de verrouillage :
- Un cadre ne peut plus modifier ses saisies une fois son mois validé (`month_locks.cadre_validated = true`), tant qu'un administrateur ne l'a pas réouvert.
- Personne (y compris l'administrateur) ne peut modifier les saisies d'une société dont le mois est validé (`company_month_validations.admin_validated = true`), tant qu'il n'a pas été réouvert.
- L'administrateur ne peut valider une société pour un mois donné que si **tous** les cadres actifs de cette société ont déjà validé leur mois.
## Développement local (sans Docker)
```bash
cd backend && npm install
# variables d'env : voir backend/.env.example (pointer PGHOST vers un Postgres local)
npm start
```
Le frontend est un simple dossier de fichiers statiques (`frontend/public`) ; en développement, servez-le avec n'importe quel serveur statique qui proxifie `/api` vers `http://localhost:4790` (voir `frontend/nginx.conf` pour la configuration de référence).