Compare commits

..
Author SHA1 Message Date
jmartinandCursor 461c70df05 refactor(front): suppression anciens widgets dashbord_parent remplaces par la coquille quotidien (#166)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 23:39:08 +02:00
jmartinandCursor fa88d00657 chore(assets): pastille corail en reserve (#166)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 23:36:06 +02:00
jmartinandCursor 6e18956a63 feat(front): coquille quotidien parent 3 colonnes + bandeau pastel (#166)
- QuotidienShell / QuotidienBandeau / QuotidienTheme reutilisables (parent, AM #169)
- Bandeau : logo, pastilles dessinees Cahier de liaison / Agenda / Contrat
  (jaune / peche / turquoise, inactive = meme couleur a 45 %), menu user sur
  pastille lavande
- Separateurs trait crayon gris (bandeau / colonnes / footer)
- ImageButton : shape stadium (focus suit le contour) + bgOpacity
- AppFooter : option showTopBorder
- ParentDashboardScreen branche sur la coquille avec placeholders
  cartes (#167/#173), blog (#178), messagerie (#184), stubs Agenda/Contrat (#187)
- Assets pastilles (meme silhouette 1190x299) et traits crayon

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 23:34:36 +02:00
jmartinandCursor 745349bc83 docs(0.2.0): mini-spec quotidien parent/AM, découpage tickets et maquettes.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 17:17:15 +02:00
jmartinandCursor 89f65356d1 docs(#117): CDC V1.4 (users à jour, reste conservé) + SRS utilisateurs.
- CDC complet conservé ; sections gestion utilisateurs / dossiers alignées v0.1.0
- Archive V1.3 + EVOLUTIONS_CDC ; nouvelle 12_SRS-GESTION-UTILISATEURS
- INDEX / versions / bilan mis à jour

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 10:22:32 +02:00
39 changed files with 2751 additions and 750 deletions
+16 -8
View File
@@ -1,17 +1,19 @@
# Index de la documentation — P'titsPas # Index de la documentation — P'titsPas
Index de navigation du dépôt. Dernière révision : **septembre 2026** (clôture doc 0.1.0). Index de navigation du dépôt. Dernière révision : **septembre 2026** (CDC V1.4 + SRS users #117).
## Produit & versions ## Produit & versions
| Doc | Contenu | | Doc | Contenu |
|-----|---------| |-----|---------|
| [01 — Cahier des charges](./01_CAHIER-DES-CHARGES.md) | CDC actuel (V1.3) — amendement via **#117** | | [01 — Cahier des charges V1.4](./01_CAHIER-DES-CHARGES.md) | CDC **complet** (cible) ; gestion utilisateurs alignée `v0.1.0` |
| [EVOLUTIONS_CDC.md](./EVOLUTIONS_CDC.md) | Écarts CDC → app (intrant amendement) | | [12 — SRS gestion utilisateurs](./12_SRS-GESTION-UTILISATEURS.md) | Spécification **technique** du domaine users / dossiers |
| [05 — Versions & milestones](./05_VERSIONS-ET-MILESTONES.md) | Semver Gitea + bilans | | [05 — Versions & milestones](./05_VERSIONS-ET-MILESTONES.md) | Semver Gitea + bilans |
| [29 — Bilan version 0.1.0](./29_BILAN-VERSION-0.1.0.md) | Tickets livrés 0.1.0 + thèmes | | [29 — Bilan version 0.1.0](./29_BILAN-VERSION-0.1.0.md) | Tickets livrés 0.1.0 |
| [30 — Découpage tickets quotidien](./30_DECOUPAGE-TICKETS-QUOTIDIEN.md) | Brouillon / backlog tickets parent/AM |
| [31 — Mini-spec quotidien parent/AM](./31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md) | Besoin figé atelier sept. 2026 |
| [04 — Roadmap générale](./04_ROADMAP-GENERALE.md) | Vision phases long terme | | [04 — Roadmap générale](./04_ROADMAP-GENERALE.md) | Vision phases long terme |
| [28 — Évolution famille / responsables](./28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md) | Modèle dossier / foyer | | [28 — Évolution famille / responsables](./28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md) | Limites modèle foyer / contournements |
## Architecture & infra ## Architecture & infra
@@ -28,15 +30,21 @@ Index de navigation du dépôt. Dernière révision : **septembre 2026** (clôtu
| Doc | Contenu | | Doc | Contenu |
|-----|---------| |-----|---------|
| [20 — Workflow création de compte](./20_WORKFLOW-CREATION-COMPTE.md) | Inscription / validation | | [20 — Workflow création de compte](./20_WORKFLOW-CREATION-COMPTE.md) | Inscription / validation (détail historique) |
| [juridique/](./juridique/README.md) | CGU / CGC / privacy + [22 technique](./juridique/22_DOCUMENTS-LEGAUX.md) | | [juridique/](./juridique/README.md) | CGU / CGC / privacy + [22 technique](./juridique/22_DOCUMENTS-LEGAUX.md) |
## Charte & maquettes
| Doc | Contenu |
|-----|---------|
| [CHARTE_GRAPHIQUE.md](./CHARTE_GRAPHIQUE.md) | Charte UI | | [CHARTE_GRAPHIQUE.md](./CHARTE_GRAPHIQUE.md) | Charte UI |
| [maquettes/](./maquettes/README.md) | Maquettes TdB quotidien (réf. v4 + historique) |
## Projet & outillage ## Projet & outillage
| Doc | Contenu | | Doc | Contenu |
|-----|---------| |-----|---------|
| [23 — Suivi tickets](./23_SUIVI-TICKETS.md) | Pointeur Gitea (plus de liste figée) | | [23 — Suivi tickets](./23_SUIVI-TICKETS.md) | Pointeur Gitea |
| [24 — Décisions projet](./24_DECISIONS-PROJET.md) | ADR / décisions | | [24 — Décisions projet](./24_DECISIONS-PROJET.md) | ADR / décisions |
| [26 — API Gitea](./26_GITEA-API.md) | Issues, PR, milestones | | [26 — API Gitea](./26_GITEA-API.md) | Issues, PR, milestones |
| [27 — Briefing frontend](./27_BRIEFING-FRONTEND.md) | Accès Git, priorités | | [27 — Briefing frontend](./27_BRIEFING-FRONTEND.md) | Accès Git, priorités |
@@ -52,7 +60,7 @@ Index de navigation du dépôt. Dernière révision : **septembre 2026** (clôtu
| Emplacement | Usage | | Emplacement | Usage |
|-------------|--------| |-------------|--------|
| [archive/](./archive/README.md) | Obsolete / temporaires | | [archive/](./archive/README.md) | Obsolete / temporaires |
| [archive/obsolete/](./archive/obsolete/) | CDC SuperNounou, ancienne liste tickets, backlog Phase 2 figé, notes ponctuelles | | [archive/obsolete/](./archive/obsolete/) | CDC V1.3, EVOLUTIONS_CDC, SuperNounou, listes figées |
## Données de test ## Données de test
+155 -70
View File
@@ -1,14 +1,19 @@
--- ---
title: "P'titsPas - Cahier des Charges Fonctionnel" title: "P'titsPas - Cahier des Charges Fonctionnel"
author: "Julien MARTIN" author: "Julien MARTIN"
date: "Novembre 2025" date: "Septembre 2026"
version: "v1.3" version: "v1.4"
--- ---
# P'titsPas – Cahier des Charges Fonctionnel # P'titsPas – Cahier des Charges Fonctionnel
> **Objet :** Définir le périmètre fonctionnel, les rôles utilisateurs, les processus métiers et les exigences techniques de la plateforme P'titsPas, destinée à accompagner les collectivités locales dans la gestion de la garde d’enfants. > **Objet :** Définir le périmètre fonctionnel, les rôles utilisateurs, les processus métiers et les exigences techniques de la plateforme P'titsPas, destinée à accompagner les collectivités locales dans la gestion de la garde d’enfants.
> **V1.4 (sept. 2026)** — Mise à jour de la **gestion des utilisateurs / dossiers / validation** pour coller au livré produit **`v0.1.0`**. Le reste du CDC (contrats, messagerie, agenda, paie, recherche AM, etc.) est **conservé** comme cible fonctionnelle.
> Détail technique du domaine utilisateurs : [12_SRS-GESTION-UTILISATEURS.md](./12_SRS-GESTION-UTILISATEURS.md).
> Bilan tickets `v0.1.0` : [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md).
> Archive V1.3 : [archive/obsolete/01_CAHIER-DES-CHARGES-v1.3.md](./archive/obsolete/01_CAHIER-DES-CHARGES-v1.3.md).
--- ---
## Historique des versions ## Historique des versions
@@ -19,6 +24,7 @@ version: "v1.3"
| 1.1 | 24/04/2025 | Julien MARTIN | Ajouts : gestion multi-enfants, fin de contrat, tableau de bord étendu | | 1.1 | 24/04/2025 | Julien MARTIN | Ajouts : gestion multi-enfants, fin de contrat, tableau de bord étendu |
| 1.2 | 26/05/2025 | Julien MARTIN | Remplacement de "SuperNounou" par "P'titsPas" | | 1.2 | 26/05/2025 | Julien MARTIN | Remplacement de "SuperNounou" par "P'titsPas" |
| 1.3 | 24/11/2025 | Julien MARTIN | Correction : retrait photo de profil parent (section 3.1.1) | | 1.3 | 24/11/2025 | Julien MARTIN | Correction : retrait photo de profil parent (section 3.1.1) |
| **1.4** | **15/09/2026** | Julien MARTIN | **Gestion utilisateurs** alignée `v0.1.0` (dossiers, validation/refus/reprise, fiches staff, suppressions, retrait naissance multiple & SMS) ; reste du CDC inchangé en cible |
--- ---
@@ -42,6 +48,8 @@ version: "v1.3"
### 3.4 Création d’un administrateur ### 3.4 Création d’un administrateur
### 3.5 Fiche enfant ### 3.5 Fiche enfant
### 3.6 Authentification et sécurité ### 3.6 Authentification et sécurité
### 3.7 Numéro de dossier, validation, refus et reprise
### 3.8 Suppressions (vue métier)
## 4. Tableaux de bord ## 4. Tableaux de bord
### 4.1 Vue d’ensemble ### 4.1 Vue d’ensemble
@@ -60,15 +68,15 @@ version: "v1.3"
#### 4.3.5 Heures supplémentaires #### 4.3.5 Heures supplémentaires
#### 4.3.6 Messagerie #### 4.3.6 Messagerie
### 4.4 Tableau de bord des gestionnaires ### 4.4 Tableau de bord des gestionnaires
#### 4.4.1 Comptes à valider #### 4.4.1 Dossiers à valider
#### 4.4.2 Liste des utilisateurs #### 4.4.2 Gestion des utilisateurs (partagée)
#### 4.4.3 Contrats #### 4.4.3 Contrats
#### 4.4.4 Messagerie #### 4.4.4 Messagerie
#### 4.4.5 Événements RPE #### 4.4.5 Événements RPE
#### 4.4.6 Alertes #### 4.4.6 Alertes
### 4.5 Tableau de bord des administrateurs ### 4.5 Tableau de bord des administrateurs
#### 4.5.1 Menu Profil #### 4.5.1 Menu Profil
#### 4.5.2 Gestion des utilisateurs #### 4.5.2 Gestion des utilisateurs (partagée admin + gestionnaire)
#### 4.5.3 Gestion des enfants #### 4.5.3 Gestion des enfants
#### 4.5.4 Paramètres de la plateforme #### 4.5.4 Paramètres de la plateforme
#### 4.5.5 Statistiques et supervision #### 4.5.5 Statistiques et supervision
@@ -193,18 +201,23 @@ Les assistantes maternelles peuvent :
Les gestionnaires (responsables de relais petite enfance) disposent d’un tableau de bord de supervision. Ils peuvent : Les gestionnaires (responsables de relais petite enfance) disposent d’un tableau de bord de supervision. Ils peuvent :
- Valider ou rejeter les demandes de création de compte - Valider ou refuser les **dossiers** (inscriptions parent / AM) et accompagner les reprises
- Suivre les mises en relation et les contrats - Gérer les usagers (fiches parent / AM / enfant, rattachements, dossiers) — socle partagé avec l’admin
- Organiser des événements ou des rendez-vous - Suivre les mises en relation et les contrats (cible CDC)
- Gérer les conflits ou les fins de contrat - Organiser des événements ou des rendez-vous (cible CDC)
- Gérer les conflits ou les fins de contrat (cible CDC)
- Lancer des sondages ou modérer un blog RPE (si activé) - Lancer des sondages ou modérer un blog RPE (si activé)
- Voir les historiques et statistiques liés à leur périmètre - Voir les historiques et statistiques liés à leur périmètre
Ils **ne créent pas** les comptes gestionnaire / administrateur (réservé à l’admin).
### 2.1.4 Administrateurs ### 2.1.4 Administrateurs
Les administrateurs sont les représentants techniques et institutionnels de la collectivité (DSI ou agents désignés). Ils peuvent : Les administrateurs sont les représentants techniques et institutionnels de la collectivité (DSI ou agents désignés). Ils peuvent :
- Créer ou supprimer des comptes (gestionnaires, parents, assistantes maternelles) - Tout ce que peut faire un gestionnaire sur les usagers / dossiers
- Créer ou supprimer des comptes **staff** (gestionnaires, administrateurs) selon garde-fous
- Créer ou supprimer des comptes usagers (parents, AM, enfants) selon droits
- Personnaliser l’interface (logo, couleurs, nom de la ville) - Personnaliser l’interface (logo, couleurs, nom de la ville)
- Activer ou désactiver des modules complémentaires - Activer ou désactiver des modules complémentaires
- Consulter les statistiques d’usage - Consulter les statistiques d’usage
@@ -245,12 +258,15 @@ Le parcours de création d’un compte parent s’effectue en plusieurs étapes
**Note** : Le parent 2 ne définit **pas** de mot de passe lors de l'inscription. Il recevra un email avec un lien pour créer son mot de passe après validation du gestionnaire. Cette approche est particulièrement adaptée aux situations de parents séparés ou divorcés où la communication peut être difficile. **Note** : Le parent 2 ne définit **pas** de mot de passe lors de l'inscription. Il recevra un email avec un lien pour créer son mot de passe après validation du gestionnaire. Cette approche est particulièrement adaptée aux situations de parents séparés ou divorcés où la communication peut être difficile.
### 3.1.3 Informations sur l'enfant ### 3.1.3 Informations sur l'enfant
- Un ou **plusieurs** enfants peuvent être ajoutés
- Prénom (facultatif si enfant à naître) - Prénom (facultatif si enfant à naître)
- Nom (hérité des parents) - Nom (hérité des parents)
- Genre (H / F) - obligatoire - Genre (H / F / Autre) — obligatoire
- Date de naissance ou **date prévisionnelle de naissance** (si l'enfant n'est pas encore né, un switch modifie le label) - Date de naissance ou **date prévisionnelle de naissance** (si l'enfant n'est pas encore né, un switch modifie le label)
- Photo obligatoire si l'enfant est né - Photo (selon règles d’inscription) et **consentement photo**
- Rattachement automatique aux deux parents - Rattachement automatique au parent 1 et au parent 2 s’il est renseigné
**Note V1.4** : il n’existe **pas** de champ « naissance multiple / jumeaux » (retiré du produit).
### 3.1.4 Présentation du dossier ### 3.1.4 Présentation du dossier
- Zone de texte libre permettant aux parents de décrire leur situation - Zone de texte libre permettant aux parents de décrire leur situation
@@ -265,10 +281,12 @@ Le parcours de création d’un compte parent s’effectue en plusieurs étapes
### 3.1.6 Récapitulatif et validation ### 3.1.6 Récapitulatif et validation
- Résumé des données saisies - Résumé des données saisies
- Vérification, puis envoi de la demande - Vérification, puis envoi de la demande
- Attribution d’un **numéro de dossier** (format type AAAA-NNNNNN)
- Les comptes parent sont soumis à validation par un gestionnaire avant activation - Les comptes parent sont soumis à validation par un gestionnaire avant activation
- Une fois validé, chaque parent (Parent 1 et Parent 2 si renseigné) reçoit un e-mail ou un SMS contenant un lien pour créer son mot de passe - Une fois validé, chaque parent (Parent 1 et Parent 2 si renseigné) reçoit un **e-mail** contenant un lien pour créer son mot de passe
- Le lien est valable pendant 7 jours - Le lien est valable pendant une durée limitée (jeton à usage unique)
- Une fois le mot de passe créé, le parent peut se connecter à son espace - Une fois le mot de passe créé, le parent peut se connecter à son espace
- En cas de **refus**, le dossier n’est pas supprimé : l’usager peut **reprendre** sa demande (lien e-mail ou numéro de dossier) — voir §3.7
## 3.2 Création de compte assistante maternelle ## 3.2 Création de compte assistante maternelle
@@ -298,7 +316,7 @@ Ce parcours est divisé en deux panneaux.
- Champ libre : message à destination du gestionnaire - Champ libre : message à destination du gestionnaire
- Permet de justifier une demande ou d’ajouter des précisions - Permet de justifier une demande ou d’ajouter des précisions
### 3.1.4 – Acceptation des CGU ### 3.2.4 – Acceptation des CGU
- Les utilisateurs doivent cocher la case - Les utilisateurs doivent cocher la case
« J’ai lu et j’accepte les Conditions Générales d’Utilisation et la Politique de confidentialité ». « J’ai lu et j’accepte les Conditions Générales d’Utilisation et la Politique de confidentialité ».
- Un lien direct ouvre la version PDF des CGU. - Un lien direct ouvre la version PDF des CGU.
@@ -307,23 +325,26 @@ Ce parcours est divisé en deux panneaux.
### 3.2.5 Récapitulatif et validation ### 3.2.5 Récapitulatif et validation
- Résumé des données saisies - Résumé des données saisies
- Vérification, puis envoi de la demande - Vérification, puis envoi de la demande
- Attribution d’un **numéro de dossier**
- Validation par un gestionnaire requise avant activation - Validation par un gestionnaire requise avant activation
- Une fois validé, l'assistante maternelle reçoit un e-mail ou un SMS contenant un lien pour créer son mot de passe - Une fois validé, l'assistante maternelle reçoit un **e-mail** contenant un lien pour créer son mot de passe
- Le lien est valable pendant 7 jours - Le lien est valable pendant une durée limitée (jeton à usage unique)
- Une fois le mot de passe créé, l'assistante maternelle peut se connecter à son espace - Une fois le mot de passe créé, l'assistante maternelle peut se connecter à son espace
- En cas de **refus** : reprise possible — voir §3.7
## 3.3 Création d’un gestionnaire ## 3.3 Création d’un gestionnaire
- Réalisée par un administrateur - Réalisée par un **administrateur** (ou super administrateur) — un gestionnaire ne crée pas de comptes staff
- Champs obligatoires : nom, prénom, adresse e-mail, mot de passe - Champs : nom, prénom, e-mail, téléphone, mot de passe, **relais principal** (optionnel)
- Affectation à un ou plusieurs relais petite enfance - Le mot de passe peut être modifié à la première connexion si la politique l’exige
- Le mot de passe doit être modifié lors de la première connexion - Formulaire unifié de création / édition (même présentation que pour les administrateurs)
## 3.4 Création d’un administrateur ## 3.4 Création d’un administrateur
- Seuls les administrateurs existants peuvent créer de nouveaux comptes administrateurs - Seuls les **administrateurs** (et super administrateur) peuvent créer de nouveaux comptes administrateurs
- Les droits sont équivalents (possibilité de restreindre par périmètre dans une version multi-mairies) - Champs : nom, prénom, e-mail, téléphone, mot de passe (pas de relais)
- Obligation de changer le mot de passe à la première connexion - Les droits sont équivalents entre administrateurs (le **super administrateur** est un compte d’installation non supprimable)
- Obligation de changer le mot de passe à la première connexion si exigé
## 3.5 Fiche enfant ## 3.5 Fiche enfant
@@ -331,22 +352,68 @@ Chaque enfant est représenté par une fiche :
- Prénom (facultatif si enfant à naître) - Prénom (facultatif si enfant à naître)
- Nom - Nom
- Genre (H / F) - obligatoire - Genre (H / F / Autre) — obligatoire
- Date de naissance ou date prévisionnelle - Date de naissance ou date prévisionnelle
- Photo (obligatoire si l'enfant est né ET si l'option *Photo obligatoire* est activée) - Photo et consentement photo selon configuration (consentement tracé)
- Consentement photo enregistré : valeur booléenne + horodatage liés à l'accord donné par le parent - Statut : à naître / actif / scolarisé (évolutions de libellés possibles ultérieurement)
- Statut : à naître / actif / scolarisé - Responsables (parents) rattachés — liens vers les fiches parent
- Indication possible : jumeaux, triplés, etc. - Assistante maternelle de rattachement (le cas échéant)
- Possibilité de rattacher un enfant à plusieurs parents (garde alternée)
**Note V1.4** : pas d’indication « jumeaux / triplés » comme champ dédié.
Les fiches enfants sont :
- Accessibles depuis la fiche parent, la fiche AM, l’onglet **Enfants** du dashboard staff
- Créables par le staff pour un foyer existant
- Partagées entre les responsables rattachés
## 3.6 Authentification et sécurité ## 3.6 Authentification et sécurité
- Tous les comptes utilisent une combinaison adresse e-mail + mot de passe - Authentification par e-mail + mot de passe
- Les gestionnaires et administrateurs doivent modifier leur mot de passe à la première connexion - Comptes **en attente** ou **suspendus** : connexion refusée
- Des mécanismes de récupération sont disponibles en cas de perte - Les gestionnaires et administrateurs doivent modifier leur mot de passe à la première connexion (si exigé)
- **Création de mot de passe** post-validation : lien e-mail (jeton TTL, usage unique)
- **Mot de passe oublié** : parcours distinct (demande → e-mail → réinitialisation)
- Pas d’envoi de lien de mot de passe par SMS (canal **e-mail** uniquement)
Un lien direct vers les **Mentions légales** et la **Politique de confidentialité** est accessible en permanence depuis le pied de page, y compris avant la connexion. Un lien direct vers les **Mentions légales** et la **Politique de confidentialité** est accessible en permanence depuis le pied de page, y compris avant la connexion.
## 3.7 Numéro de dossier, validation, refus et reprise
### Numéro de dossier
- Format type **AAAA-NNNNNN**
- Affiché dans les listes staff, les modales et les e-mails concernés
- Sert de clé métier pour validation, refus et reprise
### Validation
- Le staff (gestionnaire / administrateur) examine le dossier (wizard de revue)
- Action **Valider** : activation du circuit comptes + e-mails de création de mot de passe
### Refus
- Action **Refuser** : le dossier est refusé **sans suppression** des données
- L’usager est informé par e-mail et peut reprendre sa demande
### Reprise
- Via le **lien** reçu par e-mail, ou depuis l’écran de connexion avec le **numéro de dossier**
- Formulaire prérempli / correction des informations
- Nouvelle soumission → retour en file « à valider »
### Création et édition de dossiers par le staff
- En plus de l’inscription publique, le staff peut **créer** un dossier famille ou AM (wizard)
- Le staff peut **éditer** un dossier existant (y compris ajout d’un 2ᵉ parent pour une famille mono-parent)
## 3.8 Suppressions (vue métier)
Sous réserve des droits (détail technique : [SRS gestion utilisateurs](./12_SRS-GESTION-UTILISATEURS.md)) :
| Cible | Principe |
|-------|----------|
| Parent, AM, enfant, dossier | Gestionnaire, administrateur, super admin — avec confirmation |
| Gestionnaire | Administrateur / super admin uniquement |
| Administrateur | Admin / super admin, hors soi-même, hors cible super admin ; garde-fou sur le dernier administrateur |
| Super administrateur | Non supprimable |
Un gestionnaire ne peut pas supprimer **son propre** compte.
# 4. Tableaux de bord # 4. Tableaux de bord
Chaque rôle utilisateur dispose d’un tableau de bord personnalisé, adapté à ses fonctions dans la plateforme. Ces interfaces sont pensées pour être lisibles, fonctionnelles et évolutives. Chaque rôle utilisateur dispose d’un tableau de bord personnalisé, adapté à ses fonctions dans la plateforme. Ces interfaces sont pensées pour être lisibles, fonctionnelles et évolutives.
@@ -357,8 +424,8 @@ Chaque rôle utilisateur dispose d’un tableau de bord personnalisé, adapté
|------------------------|-------------------------------------------------------------------| |------------------------|-------------------------------------------------------------------|
| Parents | Recherche d'assistante maternelle, gestion des enfants, contrat, agenda | | Parents | Recherche d'assistante maternelle, gestion des enfants, contrat, agenda |
| Assistantes maternelles| Dossiers reçus, enfants accueillis, heures sup, agenda, messagerie| | Assistantes maternelles| Dossiers reçus, enfants accueillis, heures sup, agenda, messagerie|
| Gestionnaires | Validation de comptes, contrats, messagerie, événements RPE | | Gestionnaires | Dossiers à valider, gestion utilisateurs/enfants (partagée), contrats, messagerie, événements RPE |
| Administrateurs | Paramètres globaux, gestion des utilisateurs, statistiques | | Administrateurs | Paramètres globaux, même gestion utilisateurs (droits staff élargis), statistiques |
Les tableaux de bord intègrent : Les tableaux de bord intègrent :
- Une **barre de navigation supérieure** (liens de navigation rapide) - Une **barre de navigation supérieure** (liens de navigation rapide)
@@ -502,17 +569,24 @@ L’assistante maternelle accède à une interface dédiée à la gestion de ses
Le gestionnaire RPE dispose d’une vision transversale sur les utilisateurs, les dossiers et les activités de la structure. Son rôle est d'accompagner, superviser, et arbitrer si nécessaire. Le gestionnaire RPE dispose d’une vision transversale sur les utilisateurs, les dossiers et les activités de la structure. Son rôle est d'accompagner, superviser, et arbitrer si nécessaire.
### 4.4.1 Comptes à valider ### 4.4.1 Dossiers à valider
- File d’attente des demandes de création de compte - File d’attente des **dossiers** (famille / AM) en attente de validation
- Détails de chaque demande : parent, assistante maternelle - Affichage du **numéro de dossier** et des informations essentielles
- Actions possibles : Valider / Refuser / Demander des précisions - Ouverture en **revue** (wizard étapes) : Valider / Refuser
- Le refus n’efface pas le dossier : l’usager peut reprendre (voir §3.7)
- L’onglet **Dossiers** du dashboard regroupe aussi une **liste unifiée** des dossiers (au-delà de la seule file « à valider »)
### 4.4.2 Liste des utilisateurs ### 4.4.2 Gestion des utilisateurs (partagée)
- Filtres par rôle, statut, date d’inscription > **V1.4** — La gestion opérationnelle des usagers (parents, AM, enfants, dossiers) est **partagée** entre gestionnaire et administrateur via le même dashboard. Seule la gestion des **comptes staff** (création / suppression gestionnaire ou admin) est réservée à l’administrateur.
- Accès rapide aux informations et historiques
- Possibilité de contacter un utilisateur - Onglets : Parents, Assistantes maternelles, Enfants, Gestionnaires (selon droits), Administrateurs (admin)
- Accès aux **fiches** (identité, rattachements, capacité AM, etc.)
- Création de dossiers / enfants par le staff
- Filtres et recherche ; ouverture des fiches depuis les cartes / listes
Le détail des panneaux admin historiques est décrit en §4.5.2 (même socle fonctionnel).
### 4.4.3 Contrats ### 4.4.3 Contrats
@@ -566,47 +640,56 @@ Le menu profil de l’administrateur comprend uniquement :
Toutes les fonctionnalités de gestion sont accessibles via des onglets distincts dans le tableau de bord. Toutes les fonctionnalités de gestion sont accessibles via des onglets distincts dans le tableau de bord.
### 4.5.2 Gestion des utilisateurs ### 4.5.2 Gestion des utilisateurs (partagée admin + gestionnaire)
L’administration des utilisateurs est organisée par panneaux distincts pour chaque type de profil. L’administration des utilisateurs est organisée par panneaux / onglets. **Admin et gestionnaire** partagent les panneaux usagers ; l’admin dispose en plus des droits staff.
#### a. Gestion des gestionnaires #### a. Gestion des gestionnaires
- Liste des gestionnaires existants - Liste des gestionnaires existants
- Création (nom, prénom, e-mail, mot de passe) - Création / édition (nom, prénom, e-mail, téléphone, mot de passe, relais principal) — **administrateur uniquement** pour la création
- Attribution à un ou plusieurs RPE - Réinitialisation / nouveau mot de passe en édition
- Réinitialisation du mot de passe - Suppression du compte — **administrateur uniquement** (pas d’auto-suppression)
- Suppression du compte
#### b. Gestion des parents #### b. Gestion des parents
- Liste complète des parents enregistrés - Liste des parents enregistrés
- Recherche par nom, statut, enfants associés - Ouverture de la **fiche parent** (identité, statut, enfants, lien co-parent)
- Modification des informations - Rattacher / détacher un enfant
- Suppression d’un compte - Création de dossier famille (wizard staff)
- Consultation du statut des dossiers liés - Suppression d’un compte / dossier selon droits (§3.8)
#### c. Gestion des assistantes maternelles #### c. Gestion des assistantes maternelles
- Liste des assistantes avec numéro d’agrément - Liste des AM (agrément, capacité, etc.)
- Modification ou suppression d’un compte - **Fiche AM** (identité, professionnel, enfants accueillis)
- Filtrage par zone géographique ou capacité - Respect de la **capacité max** pour les rattachements
- Création de dossier AM (wizard staff)
- Suppression selon droits
#### d. Gestion des administrateurs #### d. Gestion des administrateurs
- Création de nouveaux comptes administrateurs - Création de nouveaux comptes administrateurs — **administrateur / super admin**
- Suivi des droits - Suivi des droits
- Obligation de modification du mot de passe à la première connexion - Obligation de modification du mot de passe à la première connexion si exigé
- Suppression avec garde-fous (pas soi-même, pas le super admin, dernier admin)
#### e. Onglet Dossiers
- Liste unifiée + dossiers à valider
- Revue / édition des dossiers (wizards)
### 4.5.3 Gestion des enfants ### 4.5.3 Gestion des enfants
Deux accès possibles à la gestion des enfants : Deux accès possibles à la gestion des enfants :
#### a. Par fiche parent #### a. Par fiche parent (ou fiche AM)
- Consultation et édition des enfants associés à chaque parent - Consultation et édition des enfants associés
- Rattacher / détacher depuis la fiche
#### b. Vue globale “Enfants” #### b. Vue globale “Enfants”
- Liste complète avec : - Liste complète avec :
- Nom, prénom, date de naissance ou prévisionnelle - Nom, prénom, date de naissance ou prévisionnelle
- Statut (à naître, actif, scolarisé) - Statut (à naître, actif, scolarisé)
- Parents associés - Parents associés (alerte si **sans responsable**)
- Contrat en cours (si applicable) - AM associée le cas échéant
- Contrat en cours (si applicable — module contrats)
- Création d’un enfant rattaché à un **foyer existant**
- Possibilité de modifier ou supprimer une fiche enfant - Possibilité de modifier ou supprimer une fiche enfant
### 4.5.4 Paramètres de la plateforme ### 4.5.4 Paramètres de la plateforme
@@ -905,10 +988,10 @@ Certains outils de la plateforme sont accessibles à plusieurs profils et favori
- Le gestionnaire (à titre d’information) - Le gestionnaire (à titre d’information)
- Contient : - Contient :
- Identité - Identité
- Photo (obligatoire si né) - Photo (selon configuration / consentement)
- Statut (à naître / actif / scolarisé) - Statut (à naître / actif / scolarisé)
- Parents associés - Parents associés
- Mention s’il s’agit de jumeaux, triplés, etc. - AM associée le cas échéant
## 6.4 Contrats et avenants ## 6.4 Contrats et avenants
@@ -1185,11 +1268,11 @@ P'titsPas s’inscrit dans une démarche de service public, avec des fondements
| **Gestionnaires** | Suivi global des situations, outils de médiation, organisation d’événements | | **Gestionnaires** | Suivi global des situations, outils de médiation, organisation d’événements |
| **Administrateurs** | Supervision complète, gouvernance multi-rôle, contrôle des données | | **Administrateurs** | Supervision complète, gouvernance multi-rôle, contrôle des données |
## 10.3 Périmètre fonctionnel de la V1 ## 10.3 Périmètre fonctionnel
Fonctionnalités incluses dès la première version : **Cible CDC (plateforme complète)** — fonctionnalités décrites dans ce document :
- Création de comptes - Création de comptes et gestion des utilisateurs / dossiers
- Recherche et sélection de nounous - Recherche et sélection d’assistantes maternelles
- Suivi des dossiers - Suivi des dossiers
- Génération de contrat et avenants - Génération de contrat et avenants
- Messagerie - Messagerie
@@ -1198,6 +1281,8 @@ Fonctionnalités incluses dès la première version :
- Gestion des heures supplémentaires - Gestion des heures supplémentaires
- Suivi RGPD et administration - Suivi RGPD et administration
**Livré produit `v0.1.0`** (voir [bilan](./29_BILAN-VERSION-0.1.0.md)) : cœur **gestion des utilisateurs** — inscription, validation/refus/reprise, auth, dashboard staff (dossiers, fiches, rattachements, suppressions). Les autres modules du CDC restent la **feuille de route**.
## 10.4 Perspectives ## 10.4 Perspectives
La structure du produit permet une montée en charge progressive : La structure du produit permet une montée en charge progressive :
+16 -6
View File
@@ -12,12 +12,12 @@ Ce fichier remplace, pour le **semver / milestones**, les anciennes tables figé
| Milestone | Rôle | Statut | | Milestone | Rôle | Statut |
|-----------|------|--------| |-----------|------|--------|
| **0.1.0** | MVP opérable (auth, inscription, dashboard dossiers/fiches, suppressions, cleanups) | **Terminée** — [bilan](./29_BILAN-VERSION-0.1.0.md) | | **0.1.0** | MVP opérable (auth, inscription, dashboard dossiers/fiches, suppressions, cleanups) | **Terminée** — [bilan](./29_BILAN-VERSION-0.1.0.md) |
| **0.2.0** | Suite produit (ex. recherche / échanges — sans contrat) | Ouverte | | **0.2.0** | **Quotidien parent / AM** (TdB 3 colonnes, cartes, blog, messagerie) — [#165](https://git.ptits-pas.fr/jmartin/petitspas/issues/165)… | **Ouverte** — [mini-spec](./31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md) · [découpage](./30_DECOUPAGE-TICKETS-QUOTIDIEN.md) |
| **0.3.0** | Contrat + planning | Ouverte | | **0.3.0** | Contrat + planning | Fermée (vide) — à réouvrir au besoin |
| **0.4.0** | Carnet de liaison | Ouverte | | **0.4.0** | Carnet de liaison | Fermée (vide) — à réouvrir au besoin |
| **0.9.0** | Hors périmètre cleanup 0.1.0 (doublons, upload, tech auth/photos, UX erreurs…) | Ouverte | | **0.9.0** | Hors périmètre cleanup 0.1.0 | Fermée (vide) |
| **1.0.0** | Release majeure Phase 1 (critères PO) | Réserve | | **1.0.0** | Release majeure (critères PO) | Fermée (réserve) |
| **Backlog transverse** | Doc étendue, CI/tests, RGPD avancé, monitoring — hors semver dédié | Ouverte | | **Backlog transverse** | Doc / CI / RGPD / monitoring | Fermée (vide) |
Liens Gitea : [milestones](https://git.ptits-pas.fr/jmartin/petitspas/milestones). Liens Gitea : [milestones](https://git.ptits-pas.fr/jmartin/petitspas/milestones).
@@ -29,6 +29,16 @@ Liens Gitea : [milestones](https://git.ptits-pas.fr/jmartin/petitspas/milestones
|---------|----------| |---------|----------|
| 0.1.0 | [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md) | | 0.1.0 | [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md) |
## Documents produit de référence (post-0.1.0)
| Doc | Rôle |
|-----|------|
| [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md) | CDC **complet** V1.4 (users mis à jour ; reste = cible) |
| [12_SRS-GESTION-UTILISATEURS.md](./12_SRS-GESTION-UTILISATEURS.md) | SRS technique domaine utilisateurs |
| [31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md](./31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md) | Besoin quotidien parent/AM (0.2.0) |
| [30_DECOUPAGE-TICKETS-QUOTIDIEN.md](./30_DECOUPAGE-TICKETS-QUOTIDIEN.md) | Mapping tickets #165–#189 |
| Archive CDC V1.3 | [archive/obsolete/01_CAHIER-DES-CHARGES-v1.3.md](./archive/obsolete/01_CAHIER-DES-CHARGES-v1.3.md) |
--- ---
## Relation avec la roadmap phases ## Relation avec la roadmap phases
+250
View File
@@ -0,0 +1,250 @@
# SRS — Gestion des utilisateurs (domaine livré v0.1.0)
**Version** : 1.0
**Date** : 15/09/2026
**Ticket** : [#117](https://git.ptits-pas.fr/jmartin/petitspas/issues/117)
**Niveau** : **technique** (dev / QA)
**CDC associé** : [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md) (V1.4 — **CDC complet** ; cette SRS ne couvre que le domaine utilisateurs)
> Périmètre SRS : comptes, auth liée, dossiers famille/AM, enfants, affiliations, dashboard staff, suppressions.
> Le CDC V1.4 conserve **l’ensemble** des chapitres cibles (contrats, messagerie, etc.) ; ils ne sont **pas** redécrits ici.
> Hors SRS technique : OpenAPI exhaustif, PRA/CI → voir [11_API](./11_API.md), [10_DATABASE](./10_DATABASE.md).
---
## 1. Glossaire
| Terme | Définition |
|-------|------------|
| **User / utilisateur** | Compte authentifiable (`utilisateurs`) avec `role` et `statut` |
| **Parent pivot** | Responsable principal du foyer à l’inscription / création staff |
| **Co-parent** | Second responsable optionnel (au plus un), lié au pivot |
| **Foyer** | Pivot + co-parent éventuel + enfants affiliés |
| **Dossier** | Unité métier identifiée par `numero_dossier` (format `AAAA-NNNNNN`) — famille ou AM |
| **Pending / en_attente** | Compte ou dossier en attente de validation staff |
| **Refus** | Rejet sans suppression ; ouvre la **reprise** |
| **Relais** | Structure RPE ; rattachement optionnel d’un gestionnaire |
| **Staff** | `gestionnaire`, `administrateur`, `super_admin` |
---
## 2. Acteurs et rôles applicatifs
| `role` | Inscription publique | Dashboard staff | Créer staff | Suppressions métier usagers | Supprimer gestionnaire | Supprimer admin |
|--------|---------------------|-----------------|-------------|----------------------------|------------------------|-----------------|
| `parent` | oui | non | non | non | non | non |
| `assistante_maternelle` | oui | non | non | non | non | non |
| `gestionnaire` | non (créé staff) | oui | non | oui* | non | non |
| `administrateur` | non | oui | oui | oui* | oui | oui** |
| `super_admin` | non (install) | oui | oui | oui* | oui | oui** |
\* Sous réserve des garde-fous (pas soi-même pour sa fiche staff, etc.).
\*\* Pas soi-même ; pas de cible `super_admin` ; dernier admin : règles `canDeleteAdministrateur` (front `staff_deletion_rights.dart`).
Réf. front : `frontend/lib/utils/staff_deletion_rights.dart`
- `canCreateStaffAccounts` → admin / super_admin
- `canDeleteMetier` → gestionnaire / admin / super_admin
- `canDeleteGestionnaire` → admin / super_admin
---
## 3. Statuts
### 3.1 Utilisateur (`statut_utilisateur_type`)
| Statut | Signification |
|--------|----------------|
| `en_attente` | Inscrit, non validé ; **login refusé** |
| `actif` | Validé / utilisable |
| `suspendu` | Bloqué ; **login refusé** |
### 3.2 Enfant (`statut_enfant_type`)
Valeurs actuelles : `a_naitre`, `actif`, `scolarise` (évolution métier « gardé / sans garde » **hors** cette SRS — ticket dédié).
### 3.3 Validation dossier
Circuit staff : revue → **valider** ou **refuser**. Refus ≠ delete. Reprise → nouveau passage en attente.
---
## 4. Modèle de données (vue synthétique)
```mermaid
flowchart TB
user[utilisateurs]
parent[parents]
am[assistantes_maternelles]
enfant[enfants]
ep[enfants_parents]
df[dossier_famille]
user --> parent
user --> am
parent --> ep
enfant --> ep
parent --> df
```
| Concept | Tables / liens clés |
|---------|---------------------|
| Compte | `utilisateurs` (role, statut, email, numero_dossier, relais…) |
| Parent | `parents` (+ `id_co_parent` optionnel) |
| AM | `assistantes_maternelles` (agrément, capacité, NIR…) |
| Enfant | `enfants` |
| Affiliation parent | `enfants_parents` (N–N) |
| Dossier famille | `dossier_famille` (+ enfants dossier) |
| Tokens MDP | tables / flux tokens création & reset (TTL, usage unique) |
Détail colonnes : [10_DATABASE.md](./10_DATABASE.md).
### Invariants
1. **Pas** de champ `est_multiple` / naissance multiple (#152).
2. Au plus **un** co-parent par fiche parent.
3. Affiliation création enfant : rattache **pivot + co-parent** s’il existe (#158).
4. Détachement du **dernier** parent → enfant **sans responsable** possible (#157).
5. Rattachement AM plafonné par **capacité** (#148 / #149).
6. `super_admin` **non supprimable**.
---
## 5. Flux
### 5.1 Inscription → validation → mot de passe
```mermaid
sequenceDiagram
participant U as Usager
participant API as API
participant S as Staff
participant M as Mail
U->>API: register parent/AM
API-->>U: numero_dossier, statut en_attente
S->>API: review / valider
API->>M: mail lien create-password
U->>API: verify token + set password
API-->>U: compte actif, login OK
```
Variante **refus** : staff refuse → mail refus → usager **reprise** (token lien ou n° dossier) → resoumission → à valider.
### 5.2 Mot de passe oublié
Flux distinct de la création post-validation : demande → e-mail → reset (#127). Tokens durcis (#123).
### 5.3 Création dossier staff
- Famille : wizard + `POST` staff parent/dossier (#129).
- AM : wizard + API staff AM (#156).
- Édition : mode `edit` wizards + PATCH fiches ; ajout co-parent `POST …/co-parent` (#135).
### 5.4 Rattachements
| Lien | Opérations |
|------|------------|
| Parent ↔ enfant | attach / detach (fiche parent, fiche enfant) |
| AM ↔ enfant | attach / detach (fiche AM, fiche enfant) ; UI capacité |
API : tickets #115 / #116 ; liste enfants enrichie #136.
---
## 6. Matrice droits × actions (synthèse)
| Action | gestionnaire | administrateur | super_admin |
|--------|:------------:|:--------------:|:-----------:|
| Voir dashboard usagers / dossiers | ✓ | ✓ | ✓ |
| Valider / refuser dossier | ✓ | ✓ | ✓ |
| Créer dossier parent / AM | ✓ | ✓ | ✓ |
| Éditer fiches parent / AM / enfant | ✓ | ✓ | ✓ |
| Rattacher / détacher enfant | ✓ | ✓ | ✓ |
| GET liste relais (combo) | ✓ | ✓ | ✓ |
| Créer gestionnaire / admin | ✗ | ✓ | ✓ |
| Supprimer parent / AM / enfant / dossier | ✓ | ✓ | ✓ |
| Supprimer gestionnaire | ✗ | ✓ | ✓ |
| Supprimer admin (garde-fous) | ✗ | ✓* | ✓* |
| Supprimer super_admin | ✗ | ✗ | ✗ |
| Supprimer son propre compte staff | ✗ | ✗ | ✗ |
\* Voir `canDeleteAdministrateur` / messages `adminDeleteBlockedReason`.
---
## 7. UI staff (points d’entrée code)
| Zone | Emplacement typique |
|------|---------------------|
| Panneau gestion users | `widgets/dashboard/user_management_panel.dart` |
| Onglets parents / AM / enfants / dossiers | `widgets/dashboard/*_management_widget.dart` |
| Fiche parent | `parent_edit_modal.dart` |
| Fiche AM | `am_edit_modal.dart` |
| Fiche enfant | `child_detail_modal.dart` |
| Wizards dossier | `parent_dossier_wizard.dart`, `am_dossier_wizard.dart` |
| Modale staff | `staff_user_form_modal.dart` (`StaffUserFormModal`) |
| Confirm suppressions | `widgets/dashboard/common/suppression_confirm_dialog.dart` |
| Champs contrôlés | `validation_detail_section.dart` (`ValidationEmailField`, `ValidationPhoneField`…) |
| Thème primaire modales | `validation_modal_theme.dart` |
Shell fiches / wizards : largeur **930**, labels au-dessus, primaire violet `ValidationModalTheme`.
---
## 8. API — groupes (renvoi)
Ne pas dupliquer OpenAPI ici. Groupes utiles au domaine :
| Groupe | Exemples d’usage |
|--------|------------------|
| Auth / register | Inscription parent & AM, login, tokens MDP, oubli MDP |
| Dossiers | `GET /dossiers/:numero`, listes à valider / unifiées, validate/refuse |
| Parents | Fiche PATCH, co-parent POST, dossier staff POST |
| AM | Fiche PATCH, dossier staff POST |
| Enfants | CRUD staff, attach/detach parent & AM |
| Users / staff | CRUD gestionnaires / admins, DELETE métier |
| Relais | `GET /relais` (autorisé gestionnaire — #151) |
| Config | setup / configuration instance |
Référence : [11_API.md](./11_API.md) (à maintenir en parallèle si écart).
---
## 9. Exigences non fonctionnelles (domaine users)
| ID | Exigence |
|----|----------|
| NFR-U1 | Tokens création / reset MDP : TTL strict, usage unique |
| NFR-U2 | Mots de passe : politique min. longueur (création staff / usager selon flux) |
| NFR-U3 | E-mails métier (validation, refus, MDP) via config SMTP instance |
| NFR-U4 | Pas de login si `en_attente` ou `suspendu` |
| NFR-U5 | Confirmations explicites avant toute suppression |
| NFR-U6 | Champs e-mail / téléphone : validation & normalisation côté UI staff (SRS UX #164) |
Hors scope immédiat : normalisation erreurs auth globale (#121), observabilité (#122), audit trail complet (#128).
---
## 10. Limites modèle famille (assumées)
Documentées pour les développeurs — détail produit : [28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md](./28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md).
- Un `numero_dossier` / user ; un seul co-parent.
- Familles recomposées multi-contextes : contournement (2ᵉ compte / composition staff) jusqu’à epic #139.
- La SRS **n’exige pas** N responsables en v0.1.0.
---
## 11. Traçabilité livré 0.1.0
Source : [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md) — 48 tickets milestone 0.1.0 (auth, inscription, fiches, dossiers, suppressions, cleanups #152/#155/#162/#164).
---
## 12. Hors périmètre de cette SRS
- Contrats, avenants, fin de contrat (restent dans le **CDC** cible)
- Messagerie, agenda, événements RPE (idem CDC)
- Paie / Pajemploi
- Recherche AM côté parent
- PRA, CI/CD, monitoring infra
- Spécification OpenAPI ligne à ligne
- Texte fonctionnel complet → [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md)
+2
View File
@@ -8,5 +8,7 @@ Ne plus maintenir de catalogue exhaustif des tickets dans le dépôt : l’état
Pour une **version livrée**, lire le bilan correspondant (ex. [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md)). Pour une **version livrée**, lire le bilan correspondant (ex. [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md)).
**Backlog en cours `0.2.0` (quotidien)** : epic [#165](https://git.ptits-pas.fr/jmartin/petitspas/issues/165) · [découpage #166–#189](./30_DECOUPAGE-TICKETS-QUOTIDIEN.md) · [mini-spec](./31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md).
Archive historique (liste Phase 1 figée, avril 2026) : Archive historique (liste Phase 1 figée, avril 2026) :
[archive/obsolete/23_LISTE-TICKETS.md](./archive/obsolete/23_LISTE-TICKETS.md). [archive/obsolete/23_LISTE-TICKETS.md](./archive/obsolete/23_LISTE-TICKETS.md).
+2 -9
View File
@@ -427,15 +427,9 @@ ptitspas-app/
``` ```
docs/ docs/
├── 00_INDEX.md ├── 00_INDEX.md
├── 01_CAHIER-DES-CHARGES.md ├── 01_CAHIER-DES-CHARGES.md # V1.4 fonctionnel
├── 02_ARCHITECTURE.md
├── 03_DEPLOYMENT.md
├── 04_ROADMAP-GENERALE.md
├── 05_VERSIONS-ET-MILESTONES.md ├── 05_VERSIONS-ET-MILESTONES.md
├── 10_DATABASE.md ├── 12_SRS-GESTION-UTILISATEURS.md
├── 11_API.md
├── 20_WORKFLOW-CREATION-COMPTE.md
├── 21_CONFIGURATION-SYSTEME.md
├── 23_SUIVI-TICKETS.md ├── 23_SUIVI-TICKETS.md
├── 24_DECISIONS-PROJET.md (ce document) ├── 24_DECISIONS-PROJET.md (ce document)
├── 26_GITEA-API.md ├── 26_GITEA-API.md
@@ -443,7 +437,6 @@ docs/
├── 28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md ├── 28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md
├── 29_BILAN-VERSION-0.1.0.md ├── 29_BILAN-VERSION-0.1.0.md
├── 99_REGLES-CODAGE.md ├── 99_REGLES-CODAGE.md
├── EVOLUTIONS_CDC.md
├── CHARTE_GRAPHIQUE.md ├── CHARTE_GRAPHIQUE.md
├── juridique/ # CGU + 22_DOCUMENTS-LEGAUX.md ├── juridique/ # CGU + 22_DOCUMENTS-LEGAUX.md
├── archive/ # obsolete / temporaires ├── archive/ # obsolete / temporaires
+3 -1
View File
@@ -3,7 +3,9 @@
**Version** : 1.1 **Version** : 1.1
**Date** : 16 juin 2026 **Date** : 16 juin 2026
**Statut** : Réflexions produit / architecture — complément au [CDC](./01_CAHIER-DES-CHARGES.md) **Statut** : Réflexions produit / architecture — complément au [CDC](./01_CAHIER-DES-CHARGES.md)
**Documents liés** : [EVOLUTIONS_CDC.md](./EVOLUTIONS_CDC.md), [24_DECISIONS-PROJET.md](./24_DECISIONS-PROJET.md), [23_SUIVI-TICKETS.md](./23_SUIVI-TICKETS.md), [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md) **Documents liés** : [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md) (V1.4), [12_SRS-GESTION-UTILISATEURS.md](./12_SRS-GESTION-UTILISATEURS.md), [24_DECISIONS-PROJET.md](./24_DECISIONS-PROJET.md), [23_SUIVI-TICKETS.md](./23_SUIVI-TICKETS.md), [29_BILAN-VERSION-0.1.0.md](./29_BILAN-VERSION-0.1.0.md)
**Archive** : ancien patch CDC → [archive/obsolete/EVOLUTIONS_CDC.md](./archive/obsolete/EVOLUTIONS_CDC.md)
--- ---
+1 -1
View File
@@ -182,7 +182,7 @@ Voir aussi [05_VERSIONS-ET-MILESTONES.md](./05_VERSIONS-ET-MILESTONES.md).
## 5. Suite documentaire ## 5. Suite documentaire
1. **Amendement CDC** — ticket **#117** : intégrer les écarts réellement livrés (dossiers staff, onglet Enfants, suppressions, retrait naissance multiple, etc.) à partir de ce bilan, [EVOLUTIONS_CDC.md](./EVOLUTIONS_CDC.md) et [28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md](./28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md). 1. **Amendement CDC (#117)** — CDC V1.4 : mise à jour **gestion utilisateurs** ; le reste du CDC reste la cible. SRS technique : [12_SRS-GESTION-UTILISATEURS.md](./12_SRS-GESTION-UTILISATEURS.md). Intrants historiques archivés : [archive/obsolete/EVOLUTIONS_CDC.md](./archive/obsolete/EVOLUTIONS_CDC.md), [28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md](./28_EVOLUTION-FAMILLE-ET-RESPONSABLES.md).
2. **Tag** `v0.1.0` sur `master` lorsque milestone fermée + ce bilan mergé. 2. **Tag** `v0.1.0` sur `master` lorsque milestone fermée + ce bilan mergé.
3. Enchaîner les milestones **0.2.0+** selon [05_VERSIONS-ET-MILESTONES.md](./05_VERSIONS-ET-MILESTONES.md). 3. Enchaîner les milestones **0.2.0+** selon [05_VERSIONS-ET-MILESTONES.md](./05_VERSIONS-ET-MILESTONES.md).
+201
View File
@@ -0,0 +1,201 @@
# Découpage tickets — Quotidien parent / AM
> **Statut :** backlog Gitea créé (**milestone `0.2.0`**, **#165–#189**) — *création anticipée avant validation PO* ; **descriptions enrichies** ensuite à partir de la mini-spec / découpage.
> **Réf. visuelle :** [maquettes/courantes/maquette-dashboard-parent-quotidien-v4.png](./maquettes/courantes/maquette-dashboard-parent-quotidien-v4.png)
> **Mini-spec :** [31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md](./31_MINI-SPEC-QUOTIDIEN-PARENT-AM.md)
> **Règle :** tickets **front** / **back** séparés · widgets partagés parent↔AM · **pas d’implé code** tant que le PO n’a pas validé le backlog.
## Intention produit (rappel)
- TdB PC en **3 colonnes** : **cartes** | **blog** (défaut) | **messagerie**
- Contexte actif = **couple** enfant–nounou (parent) / enfant–parent(s) (AM)
- Blog **indispensable** (AM + RPE) ; messagerie type WhatsApp (BABA, 2 parents = même fil AM)
- Look **papier / pastel / lignée inscription** (pas le dashboard staff)
- **Widgets partagés** parent ↔ AM (blog, messagerie, cartes, coquille, couple paramétré) — pas de double implémentation
- « Effet démo » = logiciel peuplé + sync live multi-écrans — **pas** un sous-MVP jetable
- **Hors ce découpage** (jalons suivants) : page Contrat riche (Pajemploi, CP), notifs CDC fourre-tout, masquage messages, posts blog parent, Agenda « riche » (entrée bandeau possible en stub)
## Contrainte technique front (dictée 23/09)
Les panneaux parent et AM étant **quasiment identiques**, le découpage impose des **briques réutilisables** :
| Widget / module | Tickets concernés |
|-----------------|-------------------|
| Coquille TdB 3 colonnes + bandeau | A1, B1 — B1 **réutilise** la coquille d’A1 |
| Sélecteur couple (mode parent vs AM) | A2, B2 — **un** composant, 2 modes |
| Flux de cartes | C2, C3 — **un** widget + config rôles |
| Blog (fil + composeur) | D2, D3, D4 — même brique |
| Messagerie (chat + onglets) | E4, E5, E6 — même brique |
Les tickets **[Front] AM** (B1, B2, C3, D3, E5) = **intégration / mode rôle**, pas une 2ᵉ copie du code.
## Proposition de milestone
| Champ | Valeur |
|-------|--------|
| Titre | **`0.2.0`** (rouvert) |
| Description | Quotidien parent / AM — TdB 3 colonnes, cartes, blog, messagerie |
| Label | `v0.2.0` |
## Mapping Gitea
| Id | Issue | Titre |
|----|-------|-------|
| Epic | [#165](https://git.ptits-pas.fr/jmartin/petitspas/issues/165) | Epic quotidien |
| A1 | [#166](https://git.ptits-pas.fr/jmartin/petitspas/issues/166) | [Front] Coquille TdB parent |
| A2 | [#167](https://git.ptits-pas.fr/jmartin/petitspas/issues/167) | [Front] Couple enfant–nounou |
| A3 | [#168](https://git.ptits-pas.fr/jmartin/petitspas/issues/168) | [Backend] API couples parent |
| B1 | [#169](https://git.ptits-pas.fr/jmartin/petitspas/issues/169) | [Front] Coquille TdB AM |
| B2 | [#170](https://git.ptits-pas.fr/jmartin/petitspas/issues/170) | [Front] Couple enfant–parent(s) |
| B3 | [#171](https://git.ptits-pas.fr/jmartin/petitspas/issues/171) | [Backend] API enfants AM |
| C1 | [#172](https://git.ptits-pas.fr/jmartin/petitspas/issues/172) | [Backend] API cartes |
| C2 | [#173](https://git.ptits-pas.fr/jmartin/petitspas/issues/173) | [Front] Cartes parent |
| C3 | [#174](https://git.ptits-pas.fr/jmartin/petitspas/issues/174) | [Front] Cartes AM |
| C4 | [#175](https://git.ptits-pas.fr/jmartin/petitspas/issues/175) | [Front] Déclarer absence |
| C5 | [#176](https://git.ptits-pas.fr/jmartin/petitspas/issues/176) | [Front] Formulaires AM cartes |
| D1 | [#177](https://git.ptits-pas.fr/jmartin/petitspas/issues/177) | [Backend] API blog |
| D2 | [#178](https://git.ptits-pas.fr/jmartin/petitspas/issues/178) | [Front] Blog parent |
| D3 | [#179](https://git.ptits-pas.fr/jmartin/petitspas/issues/179) | [Front] Blog AM |
| D4 | [#180](https://git.ptits-pas.fr/jmartin/petitspas/issues/180) | [Front] Blog gestionnaire |
| E1 | [#181](https://git.ptits-pas.fr/jmartin/petitspas/issues/181) | [Backend] Socle messagerie |
| E2 | [#182](https://git.ptits-pas.fr/jmartin/petitspas/issues/182) | [Backend] Mess. AM |
| E3 | [#183](https://git.ptits-pas.fr/jmartin/petitspas/issues/183) | [Backend] Mess. RPE |
| E4 | [#184](https://git.ptits-pas.fr/jmartin/petitspas/issues/184) | [Front] Messagerie parent |
| E5 | [#185](https://git.ptits-pas.fr/jmartin/petitspas/issues/185) | [Front] Messagerie AM |
| E6 | [#186](https://git.ptits-pas.fr/jmartin/petitspas/issues/186) | [Front] Mess. RPE gestionnaire |
| F1 | [#187](https://git.ptits-pas.fr/jmartin/petitspas/issues/187) | [Front] Stubs Agenda / Contrat |
| F2 | [#188](https://git.ptits-pas.fr/jmartin/petitspas/issues/188) | [Front] Swipe mobile |
| G1 | [#189](https://git.ptits-pas.fr/jmartin/petitspas/issues/189) | [Backend] Seeds quotidien |
---
## Epic A — Coquille TdB parent
### A1 — [Front] Coquille TdB parent 3 colonnes + bandeau → [#166](https://git.ptits-pas.fr/jmartin/petitspas/issues/166)
Bandeau TdB / Agenda / Contrat + menu user ; corps en 3 colonnes (cartes | blog | messagerie) ; fond papier / pastel. **Livrer la coquille comme widget/layout réutilisable** (l’AM B1 s’en branche). Remplace / refactor `ParentDashboardScreen` + `dashbord_parent/`.
### A2 — [Front] Sélecteur couple enfant–nounou → [#167](https://git.ptits-pas.fr/jmartin/petitspas/issues/167)
Composant **paramétrable** (mode parent : enfant|nounou). Dropdown si plusieurs gardes ; informatif si une seule. B2 = même widget en mode AM.
### A3 — [Back] API contexte de garde actif (couples parent)
Endpoint(s) listant les couples enfant–AM du parent connecté + couple « courant » (persistance session / préférence). Données mini : ids, noms, photos.
---
## Epic B — Coquille TdB AM (miroir)
### B1 — [Front] Coquille TdB AM 3 colonnes + bandeau → [#169](https://git.ptits-pas.fr/jmartin/petitspas/issues/169)
**Réutilise** la coquille d’A1/#166 ; point de départ `am_dashboard_screen.dart`.
### B2 — [Front] Sélecteur couple enfant–parent(s) → [#170](https://git.ptits-pas.fr/jmartin/petitspas/issues/170)
**Même widget** qu’A2 en mode AM : gauche photo enfant ; droite parent1+parent2 empilés ou un parent centré.
### B3 — [Back] API contexte enfants accueillis (AM)
Liste des enfants / foyers pour l’AM + contexte courant (symétrique A3).
---
## Epic C — Cartes (file du quotidien)
### C1 — [Back] Modèle + API cartes / événements de garde
Types V1 : absence enfant, congé AM, maladie AM (« arrêt »), sortie à valider. CRUD / transitions de statut ; liaison couple / agenda (hook minimal). Règles : absence enfant sans veto AM ; congé AM accepter/refuser (**1 parent suffit**) ; maladie AM accusé parent ; sortie **1 parent suffit**. Aucun doc médical stocké.
### C2 — [Front] Flux de cartes colonne gauche (parent)
Liste scroll pastel (palette `assets/cards/` — 7 couleurs) ; ouverture détail ; actions valider / refuser / accusé selon type. Bouton **Déclarer une absence** (+ autres actions TBD plus tard).
### C3 — [Front] Flux de cartes colonne gauche (AM)
Miroir : création congé / maladie / sortie ; lecture absences enfants du jour / à venir.
### C4 — [Front] Formulaire déclarer une absence (parent)
Saisie période (+ motif léger si besoin) → crée une carte côté AM.
### C5 — [Front] Formulaires AM congé / maladie / sortie
Création des cartes correspondantes + ciblage enfants si sortie.
---
## Epic D — Blog
### D1 — [Back] Modèle + API blog (posts + médias)
Posts par auteur AM ou gestionnaire (RPE) ; texte + photos ; ciblage enfants (AM) / audience (RPE) ; fil visible parents (et AM selon règles). Sync temps réel ou polling acceptable V1.
### D2 — [Front] Colonne Blog parent (défaut)
Fil de posts ; auteur **distinct** AM vs RPE ; miniatures ; pas de bouton « Écrire un post » parent en V1 (reporté).
### D3 — [Front] Colonne Blog AM — lecture + écrire un post
Composer texte + photos + sélection enfants concernés ; publication.
### D4 — [Front] Publication blog gestionnaire (point d’entrée staff)
UI minimale côté dashboard gestionnaire (ou réutilisation) pour publier une annonce RPE visible sur les TdB parent/AM.
---
## Epic E — Messagerie
### E1 — [Back] Socle messagerie (choix lib / protocole) + API
Évaluer brique existante (style chat WhatsApp : texte, emoji, images). Conversations, participants, pièces jointes images. Temps réel (WS ou équivalent) souhaité pour l’effet multi-écrans.
### E2 — [Back] Mess. AM — conversation foyer ↔ AM
Une conversation par couple/foyer ; **les deux parents** voient le **même** fil. Pas de masquage V1.
### E3 — [Back] Mess. RPE — privée + ajout de participants
Fil 1↔1 par défaut (parent↔RPE ou AM↔RPE) ; possibilité d’**ajouter** 2ᵉ parent / AM / parent pour médiation.
### E4 — [Front] Colonne messagerie parent (onglets Mess. AM | Mess. RPE)
UI chat + saisie + PJ ; défaut = Mess. AM.
### E5 — [Front] Colonne messagerie AM (onglets équivalents)
Miroir parent ; Mess. AM = foyer courant ; Mess. RPE = canal relais.
### E6 — [Front] Mess. RPE côté gestionnaire (entrée minimale)
Liste / réponse aux fils RPE + ajout de participants pour médiation.
---
## Epic F — Navigation & mobile (socle)
### F1 — [Front] Navigation Agenda / Contrat (stubs ou routes)
Entrées bandeau : pages **placeholder** ou squelette (Agenda / Contrat pleine page) pour ne pas bloquer le TdB — contenu métier = jalons suivants.
### F2 — [Front] Adaptation mobile — swipe 3 panneaux
Sur téléphone : swipe Blog ↔ Cartes ↔ Messagerie ; navigation TdB/Agenda/Contrat hors swipe. Contrat peut rester limité (« mieux sur PC ») en V1 si besoin.
---
## Epic G — Données de démo / peuplement
### G1 — [Back/Outillage] Jeu de données peuplé quotidien
Scripts / seeds : foyer 2 parents, AM, enfant(s), quelques cartes, posts blog, fils messagerie — pour enchaîner une démo multi-écrans sans saisie manuelle lourde.
---
## Hors découpage (volontairement)
| Sujet | Motif |
|-------|--------|
| Page Contrat (Pajemploi, CP, avenants) | Module valué — jalon dédié |
| Agenda calendrier riche | Entrée bandeau OK en stub ; métier plus tard |
| Notifs CDC (contrat/paiement/dossier) | Plus tard |
| Masquage messages / privé parent↔AM | Plus tard |
| Parent auteur de posts blog | Hypothétique / plus tard |
| Photo parent à l’inscription | Non ; éventuel menu profil plus tard |
---
## Ordre de réalisation suggéré
1. **A1 → A2 → A3** (coquille + contexte parent)
2. **C1 → C2 / C4** (premières cartes utiles)
3. **D1 → D2 / D3** (blog central)
4. **E1 → E2 → E4** (messagerie AM)
5. **B1 → B2 → B3** + miroirs C3/C5/D3/E5 (AM)
6. **E3 / E5 / E6 / D4** (RPE)
7. **F1 → F2** + **G1** (nav, mobile, seeds)
---
## Validation
Backlog créé sur Gitea (**#165–#189**, milestone `0.2.0`).
**Implémentation code** : seulement après relecture PO / ajustements éventuels sur le découpage.
+104
View File
@@ -0,0 +1,104 @@
# Mini-spec — Quotidien parent / AM
> **Statut :** figée pour backlog (sept. 2026)
> **Réf. visuelle :** [maquettes/courantes/maquette-dashboard-parent-quotidien-v4.png](./maquettes/courantes/maquette-dashboard-parent-quotidien-v4.png)
> **Découpage tickets :** [30_DECOUPAGE-TICKETS-QUOTIDIEN.md](./30_DECOUPAGE-TICKETS-QUOTIDIEN.md)
> **CDC :** vision d’origine conservée ; priorités UX évoluées (blog central, couple enfant–nounou)
## 1. Objectif
Construire les **espaces parent et AM du quotidien** : tableau de bord 3 colonnes, file de cartes (absences / congés / sorties), **blog** (cœur du jour), **messagerie** AM et RPE. Logiciel réel peuplé de données ; une démo = sync live multi-écrans (parent / AM / gestionnaire).
## 2. UI — contrainte non négociable
- Look **papier / pastel**, lignée **login / inscription** (`paper2.png`), pas le dashboard staff violet Material
- PC paysage : **3 colonnes** égales
- Mobile : **swipe** entre les 3 panneaux ; TdB / Agenda / Contrat via nav dédiée
### Architecture front — maximiser les widgets partagés
Parent et AM sont **quasi identiques** : **ne pas dupliquer** les colonnes métier.
| Brique | Usage |
|--------|--------|
| Widget **Blog** | Colonne milieu parent **et** AM (+ entrée publication gestionnaire qui réutilise le composeur) |
| Widget **Messagerie** | Colonne droite parent **et** AM (+ vue gestionnaire RPE) |
| Widget **flux de cartes** | Colonne gauche parent **et** AM (actions / droits selon rôle) |
| Widget **bandeau couple** | Variante *enfant\|nounou* (parent) vs *enfant\|parent(s)* (AM) — même composant paramétré |
| Coquille TdB 3 colonnes | Layout / bandeau navig commun ; le rôle injecte le couple + droits |
Principe : **briques correctement encapsulées** (API claire props / callbacks), branchées sur le même back ; seules les **différences de rôle** (qui crée quoi, libellés d’action) restent hors du widget partagé.
## 3. Layout TdB
| Colonne | Contenu |
|---------|---------|
| **Gauche** | Couple actif · flux de **cartes** · actions (ex. Déclarer une absence) |
| **Milieu** | **Blog** — affichage **par défaut** à l’arrivée |
| **Droite** | **Messagerie** seule — onglets Mess. AM (défaut) \| Mess. RPE |
Bandeau : **TdB** · **Agenda** · **Contrat** · menu user (recherche AM, paramètres…).
### Parent — couple
- Un bandeau **enfant | nounou** (photos + noms)
- Plusieurs gardes → dropdown unique (bascule de couple)
- Une seule garde → affichage informatif (pas de chevron)
### AM — couple (miroir)
- Bandeau **enfant | parent(s)**
- 2 parents : noms empilés à droite ; 1 parent : nom centré
- Pas de photo parent obligatoire (option profil plus tard)
## 4. Canaux (ne pas mélanger)
| Canal | Rôle |
|-------|------|
| **Cartes** | Décisions / futur proche : absences, congés, maladie AM, sorties à valider |
| **Blog** | Mémoire / actus du quotidien (texte + photos) — **indispensable**, plus un module optionnel CDC §9 |
| **Mess. AM** | Chat foyer ↔ AM (style WhatsApp : texte, emoji, images) |
| **Mess. RPE** | Privée par défaut ; ajout de participants pour médiation / conflit |
## 5. Règles métier V1
| Type | Qui initie | Validation |
|------|------------|------------|
| Absence enfant | Parent | Pas de veto AM |
| Congé AM | AM | **1 parent** accepte ou refuse |
| Maladie AM | AM | Parent accuse réception (pas de doc médical in-app) |
| Sortie | AM / RPE | **1 parent** suffit (présence / absence) ; explication aussi via blog |
- Mess. AM : les **2 parents** voient le **même** fil — **pas** de masquage V1
- Blog auteurs V1 : **AM** + **gestionnaire (RPE)** — parent auteur = plus tard
- Couple actif filtre cartes + messagerie + blog (hypothèse produit : **oui**)
## 6. Hors périmètre de ce jalon
- Page **Contrat** riche (Pajemploi, CP…) — stub bandeau OK
- **Agenda** calendrier riche — stub bandeau OK
- Notifications CDC fourre-tout (paiement, dossier…)
- Masquage messages ; posts blog parent
- Carnet repas/sieste (roadmap Phase 4)
## 7. Critères « effet démo » (acceptation)
Avec données peuplées, sur au moins 2 supports :
1. Parent et AM voient le **même contexte de garde** (couple / enfant)
2. Parent déclare une **absence** → carte visible côté AM
3. AM publie un **post blog** (texte + photo) → visible colonne blog parent
4. Gestionnaire publie une **annonce RPE** → visible sur les TdB concernés
5. Message **Mess. AM** d’un côté → apparaît de l’autre (près temps réel)
6. Look conforme charte papier / pastel
## 8. Point de départ code
- Parent : `frontend/lib/screens/home/parent_screen/ParentDashboardScreen.dart` + `dashbord_parent/`
- AM : `frontend/lib/screens/am/am_dashboard_screen.dart` (placeholder)
- Cartes couleurs : `frontend/assets/cards/` (7 teintes max)
## 9. Ordre de build suggéré
Voir [30_DECOUPAGE-TICKETS-QUOTIDIEN.md](./30_DECOUPAGE-TICKETS-QUOTIDIEN.md) § ordre — A (coquille parent) → C (cartes) → D (blog) → E (messagerie) → B (miroir AM) → RPE → F/G (mobile + seeds).
+1 -1
View File
@@ -5,7 +5,7 @@ Fichiers **hors références actives** : brouillons livrés, CDC historiques, li
## Règle de nommage (racine `docs/`) ## Règle de nommage (racine `docs/`)
- Documents **normatifs** : préfixe **`NN_`**. - Documents **normatifs** : préfixe **`NN_`**.
- Exceptions héritage listées dans [00_INDEX.md](../00_INDEX.md) (`CHARTE_GRAPHIQUE.md`, `EVOLUTIONS_CDC.md`). - Exceptions héritage listées dans [00_INDEX.md](../00_INDEX.md) (`CHARTE_GRAPHIQUE.md`).
## Sous-dossiers ## Sous-dossiers
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -4,13 +4,15 @@ Ancienne documentation **déplacée** depuis `docs/` :
| Fichier | Motif | | Fichier | Motif |
|---------|--------| |---------|--------|
| `01_CAHIER-DES-CHARGES-v1.3.md` | CDC V1.3 — remplacé par [01 V1.4](../../01_CAHIER-DES-CHARGES.md) |
| `EVOLUTIONS_CDC.md` | Patch CDC — absorbé dans V1.4 + [12_SRS](../../12_SRS-GESTION-UTILISATEURS.md) |
| `PROCEDURE-API-GITEA.md` | Doublon de [26_GITEA-API.md](../../26_GITEA-API.md) | | `PROCEDURE-API-GITEA.md` | Doublon de [26_GITEA-API.md](../../26_GITEA-API.md) |
| `ARCHITECTURE_TECHNIQUE.md` | Remplacé par [02_ARCHITECTURE.md](../../02_ARCHITECTURE.md) | | `ARCHITECTURE_TECHNIQUE.md` | Remplacé par [02_ARCHITECTURE.md](../../02_ARCHITECTURE.md) |
| `STATUS-APPLICATION.md` | Instantané daté | | `STATUS-APPLICATION.md` | Instantané daté |
| `23_LISTE-TICKETS.md` | Liste Phase 1 figée (avr. 2026) — suivi = Gitea + bilans | | `23_LISTE-TICKETS.md` | Liste Phase 1 figée — suivi = Gitea + bilans |
| `25_PHASE-2-BACKLOG.md` | Backlog technique figé — voir [05_VERSIONS…](../../05_VERSIONS-ET-MILESTONES.md) | | `25_PHASE-2-BACKLOG.md` | Backlog technique figé — voir [05_VERSIONS…](../../05_VERSIONS-ET-MILESTONES.md) |
| `SuperNounou_*` | CDC / SSS historiques | | `SuperNounou_*` | CDC / SSS historiques |
| `14_NOTE-BACKEND-CONFIG-SETUP.md` | Note ticket ponctuelle | | `14_NOTE-BACKEND-CONFIG-SETUP.md` | Note ticket ponctuelle |
| `92_NOTE-BACKEND-GESTIONNAIRES.md` | Note ticket ponctuelle | | `92_NOTE-BACKEND-GESTIONNAIRES.md` | Note ticket ponctuelle |
Mémoire produit des versions livrées : [29_BILAN-VERSION-0.1.0.md](../../29_BILAN-VERSION-0.1.0.md). Références actives : [01 CDC V1.4](../../01_CAHIER-DES-CHARGES.md), [12 SRS users](../../12_SRS-GESTION-UTILISATEURS.md), [29 bilan 0.1.0](../../29_BILAN-VERSION-0.1.0.md).
+29
View File
@@ -0,0 +1,29 @@
# Maquettes — espaces parent / AM (quotidien)
Références visuelles issues de l’atelier de cadrage (sept. 2026).
## Organisation
| Dossier | Contenu |
|---------|---------|
| [`courantes/`](./courantes/) | **Référence active** — à utiliser pour tickets / implémentation |
| [`historique/`](./historique/) | Itérations précédentes + croquis manuscrit (conservées pour traçabilité) |
## Référence active
| Fichier | Description |
|---------|-------------|
| [`courantes/maquette-dashboard-parent-quotidien-v4.png`](./courantes/maquette-dashboard-parent-quotidien-v4.png) | TdB parent PC — **3 colonnes** : cartes + couple · blog (défaut) · messagerie (AM / RPE) |
## Historique
| Fichier | Note |
|---------|------|
| [`historique/croquis-dashboard-parent-2026-09-23.jpg`](./historique/croquis-dashboard-parent-2026-09-23.jpg) | Wireframe manuscrit (WhatsApp) — point de départ layout |
| `…-v1.png` | Première génération IA (~50/50) |
| `…-v2.png` | Recadrage 1/3 + 2/3 (onglets Mess/Blog à droite) |
| `…-v3.png` | Couple enfant–nounou (encore 2 colonnes) |
## Suite éventuelle
- Maquette **TdB AM** (miroir : bandeau enfant \| parent(s)) — pas encore générée.
Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

@@ -1,41 +1,26 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:p_tits_pas/controllers/parent_dashboard_controller.dart';
import 'package:p_tits_pas/models/user.dart'; import 'package:p_tits_pas/models/user.dart';
import 'package:p_tits_pas/services/auth_service.dart'; import 'package:p_tits_pas/services/auth_service.dart';
import 'package:p_tits_pas/services/dashboardService.dart'; import 'package:p_tits_pas/widgets/quotidien/quotidien_shell.dart';
import 'package:p_tits_pas/widgets/app_footer.dart'; import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart';
import 'package:p_tits_pas/widgets/dashbord_parent/children_sidebar.dart';
import 'package:p_tits_pas/widgets/dashbord_parent/wid_dashbord.dart';
import 'package:p_tits_pas/widgets/dashboard/dashboard_bandeau.dart';
import 'package:p_tits_pas/widgets/main_content_area.dart';
import 'package:p_tits_pas/widgets/messaging_sidebar.dart';
import 'package:provider/provider.dart';
/// Tableau de bord parent — coquille 3 colonnes quotidien (#166).
/// Métier cartes / blog / messagerie : tickets C/D/E.
class ParentDashboardScreen extends StatefulWidget { class ParentDashboardScreen extends StatefulWidget {
const ParentDashboardScreen({Key? key}) : super(key: key); const ParentDashboardScreen({super.key});
@override @override
State<ParentDashboardScreen> createState() => _ParentDashboardScreenState(); State<ParentDashboardScreen> createState() => _ParentDashboardScreenState();
} }
class _ParentDashboardScreenState extends State<ParentDashboardScreen> { class _ParentDashboardScreenState extends State<ParentDashboardScreen> {
int selectedIndex = 0; QuotidienNavSection _section = QuotidienNavSection.liaison;
AppUser? _user; AppUser? _user;
void onTabChange(int index) {
setState(() {
selectedIndex = index;
});
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadUser(); _loadUser();
// Initialiser les données du dashboard
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<ParentDashboardController>().initDashboard();
});
} }
Future<void> _loadUser() async { Future<void> _loadUser() async {
@@ -43,222 +28,56 @@ class _ParentDashboardScreenState extends State<ParentDashboardScreen> {
if (mounted) setState(() => _user = user); if (mounted) setState(() => _user = user);
} }
Widget _getBody() { String get _displayName {
switch (selectedIndex) { final n = _user?.fullName.trim() ?? '';
case 0: if (n.isNotEmpty) return n;
return Dashbord_body(); final email = _user?.email.trim() ?? '';
case 1: if (email.isNotEmpty) return email.split('@').first;
return const Center(child: Text("🔍 Trouver une nounou")); return 'Parent';
case 2: }
return const Center(child: Text("⚙️ Paramètres"));
default: void _soon(String label) {
return const Center(child: Text("Page non trouvée")); ScaffoldMessenger.of(context).showSnackBar(
} SnackBar(content: Text('$label — à venir')),
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider( return QuotidienShell(
create: (context) => ParentDashboardController(DashboardService())..initDashboard(), selectedSection: _section,
child: Scaffold( onSectionSelected: (s) => setState(() => _section = s),
appBar: PreferredSize( userDisplayName: _displayName,
preferredSize: const Size.fromHeight(60.0), userEmail: _user?.email,
child: DashboardBandeau( onProfileTap: () => _soon('Profil'),
tabItems: const [ onSearchAmTap: () => _soon('Recherche AM'),
DashboardTabItem(label: 'Mon tableau de bord'), onSettingsTap: () => _soon('Paramètres'),
DashboardTabItem(label: 'Trouver une nounou'), leftColumn: const QuotidienColumnPlaceholder(
DashboardTabItem(label: 'Paramètres'), title: 'Cartes',
], subtitle:
selectedTabIndex: selectedIndex, 'Couple enfant–nounou et flux de cartes\n(à brancher — tickets #167 / #173).',
onTabSelected: onTabChange, icon: Icons.style_outlined,
userDisplayName: _user?.fullName.isNotEmpty == true
? _user!.fullName
: 'Parent',
userEmail: _user?.email,
userRole: _user?.role,
onProfileTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Modification du profil – à venir')),
);
},
onSettingsTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Paramètres – à venir')),
);
},
onLogout: () {},
showLogoutConfirmation: true,
),
),
body: Column(
children: [
Expanded(child: _getBody()),
const AppFooter(),
],
), ),
), centerColumn: const QuotidienColumnPlaceholder(
); title: 'Blog',
} subtitle:
'Fil du quotidien (affichage par défaut)\n(à brancher — ticket #178).',
Widget _buildResponsiveBody(BuildContext context, ParentDashboardController controller) { icon: Icons.auto_stories_outlined,
return LayoutBuilder( ),
builder: (context, constraints) { rightColumn: const QuotidienColumnPlaceholder(
if (constraints.maxWidth < 768) { title: 'Messagerie',
// Layout mobile : colonnes empilées subtitle:
return _buildMobileLayout(controller); 'Mess. AM · Mess. RPE\n(à brancher — ticket #184).',
} else if (constraints.maxWidth < 1024) { icon: Icons.chat_bubble_outline,
// Layout tablette : 2 colonnes ),
return _buildTabletLayout(controller); agendaBody: const QuotidienStubPage(
} else { title: 'Agenda',
// Layout desktop : 3 colonnes message: 'Agenda — contenu à venir (stub #187).',
return _buildDesktopLayout(controller); ),
} contratBody: const QuotidienStubPage(
}, title: 'Contrat',
); message: 'Contrat — contenu à venir (stub #187).',
}
Widget _buildDesktopLayout(ParentDashboardController controller) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Sidebar gauche - Enfants
SizedBox(
width: 280,
child: ChildrenSidebar(
children: controller.children,
selectedChildId: controller.selectedChildId,
onChildSelected: controller.selectChild,
onAddChild: controller.showAddChildModal,
),
),
// Contenu central
Expanded(
flex: 2,
child: MainContentArea(
selectedChild: controller.selectedChild,
selectedAssistant: controller.selectedAssistant,
events: controller.upcomingEvents,
contracts: controller.contracts,
),
),
// Sidebar droite - Messagerie
SizedBox(
width: 320,
child: MessagingSidebar(
conversations: controller.conversations,
notifications: controller.notifications,
),
),
],
);
}
Widget _buildTabletLayout(ParentDashboardController controller) {
return Row(
children: [
// Sidebar enfants plus étroite
SizedBox(
width: 240,
child: ChildrenSidebar(
children: controller.children,
selectedChildId: controller.selectedChildId,
onChildSelected: controller.selectChild,
onAddChild: controller.showAddChildModal,
isCompact: true,
),
),
// Contenu principal avec messagerie intégrée
Expanded(
child: Column(
children: [
Expanded(
flex: 2,
child: MainContentArea(
selectedChild: controller.selectedChild,
selectedAssistant: controller.selectedAssistant,
events: controller.upcomingEvents,
contracts: controller.contracts,
),
),
SizedBox(
height: 200,
child: MessagingSidebar(
conversations: controller.conversations,
notifications: controller.notifications,
isCompact: true,
),
),
],
),
),
],
);
}
Widget _buildMobileLayout(ParentDashboardController controller) {
return DefaultTabController(
length: 4,
child: Column(
children: [
// Navigation par onglets sur mobile
Container(
color: Theme.of(context).primaryColor.withOpacity(0.1),
child: const TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Enfants', icon: Icon(Icons.child_care)),
Tab(text: 'Planning', icon: Icon(Icons.calendar_month)),
Tab(text: 'Contrats', icon: Icon(Icons.description)),
Tab(text: 'Messages', icon: Icon(Icons.message)),
],
),
),
Expanded(
child: TabBarView(
children: [
// Onglet Enfants
ChildrenSidebar(
children: controller.children,
selectedChildId: controller.selectedChildId,
onChildSelected: controller.selectChild,
onAddChild: controller.showAddChildModal,
isMobile: true,
),
// Onglet Planning
MainContentArea(
selectedChild: controller.selectedChild,
selectedAssistant: controller.selectedAssistant,
events: controller.upcomingEvents,
contracts: controller.contracts,
showOnlyCalendar: true,
),
// Onglet Contrats
MainContentArea(
selectedChild: controller.selectedChild,
selectedAssistant: controller.selectedAssistant,
events: controller.upcomingEvents,
contracts: controller.contracts,
showOnlyContracts: true,
),
// Onglet Messages
MessagingSidebar(
conversations: controller.conversations,
notifications: controller.notifications,
isMobile: true,
),
],
),
),
],
), ),
); );
} }
} }
+8 -4
View File
@@ -5,7 +5,11 @@ import 'package:p_tits_pas/models/m_dashbord/child_model.dart';
import 'package:p_tits_pas/services/bug_report_service.dart'; import 'package:p_tits_pas/services/bug_report_service.dart';
class AppFooter extends StatelessWidget { class AppFooter extends StatelessWidget {
const AppFooter({Key? key}) : super(key: key); /// Ligne grise droite au-dessus du footer. À désactiver quand l'écran
/// fournit déjà son propre séparateur (ex. trait crayon du quotidien).
final bool showTopBorder;
const AppFooter({Key? key, this.showTopBorder = true}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -13,9 +17,9 @@ class AppFooter extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.white, // color: Colors.white,
border: Border( border: showTopBorder
top: BorderSide(color: Colors.grey.shade300), ? Border(top: BorderSide(color: Colors.grey.shade300))
), : null,
), ),
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
@@ -1,58 +0,0 @@
import 'package:flutter/material.dart';
class Childrensidebarwidget extends StatelessWidget{
final void Function(String childId) onChildSelected;
const Childrensidebarwidget({
Key? key,
required this.onChildSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final children = [
{'id': '1', 'name': 'Léna', 'photo': null, 'status': 'Actif'},
{'id': '2', 'name': 'Noé', 'photo': null, 'status': 'Inactif'},
];
return Container(
color: const Color(0xFFF7F7F7),
padding: const EdgeInsets.all(16),
child: Column(
children: [
// Avatar parent + bouton
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const CircleAvatar(radius: 24, child: Icon(Icons.person)),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
// Naviguer vers ajout d'enfant
},
)
],
),
const SizedBox(height: 16),
const Text("Mes enfants", style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
// Liste des enfants
...children.map((child) {
return GestureDetector(
onTap: () => onChildSelected(child['id']!),
child: Card(
color: child['status'] == 'Actif' ? Colors.teal.shade50 : Colors.white,
child: ListTile(
leading: const CircleAvatar(child: Icon(Icons.child_care)),
title: Text(child['name']!),
subtitle: Text(child['status']!),
),
),
);
}).toList()
],
),
);
}
}
@@ -1,28 +0,0 @@
import 'package:flutter/material.dart';
class AppLayout extends StatelessWidget {
final PreferredSizeWidget appBar;
final Widget body;
final Widget? footer;
const AppLayout({
Key? key,
required this.appBar,
required this.body,
this.footer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F7FA),
appBar: appBar,
body: Column(
children: [
Expanded(child: body),
if (footer != null) footer!,
],
),
);
}
}
@@ -1,203 +0,0 @@
import 'package:flutter/material.dart';
import 'package:p_tits_pas/models/m_dashbord/child_model.dart';
class ChildrenSidebar extends StatelessWidget {
final List<ChildModel> children;
final String? selectedChildId;
final Function(String) onChildSelected;
final VoidCallback onAddChild;
final bool isCompact;
final bool isMobile;
const ChildrenSidebar({
Key? key,
required this.children,
this.selectedChildId,
required this.onChildSelected,
required this.onAddChild,
this.isCompact = false,
this.isMobile = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(isMobile ? 16 : 24),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(context),
const SizedBox(height: 20),
_buildAddChildButton(context),
const SizedBox(height: 16),
Expanded(child: _buildChildrenList()),
],
),
);
}
Widget _buildHeader(BuildContext context) {
return Row(
children: [
// UserAvatar(
// size: isCompact ? 40 : 60,
// name: 'Emma Dupont', // TODO: Récupérer depuis le contexte utilisateur
// ),
if (!isCompact) ...[
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'Emma Dupont',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Icon(Icons.keyboard_arrow_down),
],
),
),
],
],
);
}
Widget _buildAddChildButton(BuildContext context) {
return SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: onAddChild,
icon: const Icon(Icons.add),
label: Text(isCompact ? 'Ajouter' : 'Ajouter un enfant'),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: 16,
vertical: isCompact ? 8 : 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
);
}
Widget _buildChildrenList() {
if (children.isEmpty) {
return const Center(
child: Text(
'Aucun enfant ajouté',
style: TextStyle(
color: Colors.grey,
fontSize: 14,
),
),
);
}
return ListView.separated(
itemCount: children.length,
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemBuilder: (context, index) {
final child = children[index];
final isSelected = child.id == selectedChildId;
return _buildChildCard(context, child, isSelected);
},
);
}
Widget _buildChildCard(BuildContext context, ChildModel child, bool isSelected) {
return InkWell(
onTap: () => onChildSelected(child.id),
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF9CC5C0).withOpacity(0.1) : Colors.transparent,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected ? const Color(0xFF9CC5C0) : Colors.grey.shade300,
width: isSelected ? 2 : 1,
),
),
child: Row(
children: [
// UserAvatar(
// // size: isCompact ? 32 : 40,
// // name: child.fullName,
// // imageUrl: child.photoUrl,
// ),
if (!isCompact) ...[
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
child.firstName,
style: TextStyle(
fontSize: 14,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
),
),
const SizedBox(height: 4),
_buildChildStatus(child.status),
],
),
),
],
],
),
),
);
}
Widget _buildChildStatus(ChildStatus status) {
String label;
Color color;
switch (status) {
case ChildStatus.withAssistant:
label = 'En garde';
color = Colors.green;
break;
case ChildStatus.available:
label = 'Disponible';
color = Colors.blue;
break;
case ChildStatus.onHoliday:
label = 'En vacances';
color = Colors.orange;
break;
case ChildStatus.sick:
label = 'Malade';
color = Colors.red;
break;
case ChildStatus.searching:
label = 'Recherche AM';
color = Colors.purple;
break;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
label,
style: TextStyle(
fontSize: 11,
color: color,
fontWeight: FontWeight.w500,
),
),
);
}
}
@@ -1,31 +0,0 @@
import 'package:flutter/material.dart';
import 'package:p_tits_pas/widgets/dashbord_parent/ChildrenSidebarwidget.dart';
import 'package:p_tits_pas/widgets/dashbord_parent/children_sidebar.dart';
import 'package:p_tits_pas/widgets/dashbord_parent/wid_mainContentArea.dart';
import 'package:p_tits_pas/widgets/messaging_sidebar.dart';
Widget Dashbord_body() {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 1️⃣ Colonne de gauche : enfants
SizedBox(
width: 250,
child: Childrensidebarwidget(
onChildSelected: (childId) {
// Met à jour l'enfant sélectionné
// Tu peux stocker cet ID dans un state `selectedChildId`
},
),
),
Expanded(
flex: 2,
child: WMainContentArea(
// Passe l’enfant sélectionné si besoin
),
),
],
);
}
@@ -1,94 +0,0 @@
import 'package:flutter/material.dart';
import 'package:p_tits_pas/widgets/messaging_sidebar.dart';
class WMainContentArea extends StatelessWidget {
const WMainContentArea({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 🔷 Informations assistante maternelle (ligne complète)
Card(
margin: const EdgeInsets.only(bottom: 16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const CircleAvatar(
radius: 30,
backgroundImage: AssetImage("assets/images/am_photo.jpg"), // à adapter
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Julie Dupont", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text("Taux horaire : 10€/h"),
Text("Frais journaliers : 5€"),
],
),
),
ElevatedButton(
onPressed: () {
// Ouvrir le contrat
},
child: const Text("Voir le contrat"),
)
],
),
),
),
// 🔷 Deux colonnes : planning + messagerie
Expanded(
child: Row(
children: [
// 📆 Planning de garde
Expanded(
flex: 2,
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Planning de garde", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Expanded(
child: Center(
child: Text("Composant calendrier à intégrer ici"),
),
)
],
),
),
),
),
const SizedBox(width: 16),
// 💬 Messagerie
Expanded(
flex: 1,
child: MessagingSidebar(
conversations: [],
notifications: [],
isCompact: false,
isMobile: false,
),
),
],
),
)
],
),
);
}
}
+14 -2
View File
@@ -10,6 +10,14 @@ class ImageButton extends StatelessWidget {
final VoidCallback onPressed; final VoidCallback onPressed;
final double fontSize; // Ajout pour la flexibilité final double fontSize; // Ajout pour la flexibilité
/// Forme utilisée pour le focus / hover / splash. Les fonds « dessinés »
/// sont des pastilles : le stadium suit leur contour au lieu d'un rectangle.
final OutlinedBorder shape;
/// Opacité du fond seul (le texte reste net) : permet un état « inactif »
/// plus clair sans changer d'asset.
final double bgOpacity;
const ImageButton({ const ImageButton({
super.key, super.key,
required this.bg, required this.bg,
@@ -19,6 +27,8 @@ class ImageButton extends StatelessWidget {
required this.textColor, required this.textColor,
required this.onPressed, required this.onPressed,
this.fontSize = 16, // Valeur par défaut this.fontSize = 16, // Valeur par défaut
this.shape = const StadiumBorder(),
this.bgOpacity = 1.0,
}); });
@override @override
@@ -36,14 +46,16 @@ class ImageButton extends StatelessWidget {
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: shape: shape,
const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
), ),
child: Ink( child: Ink(
// Pas de découpe du PNG : le trait « dessiné » déborde un peu du
// stadium, on ne rogne que le focus / hover / splash.
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage(bg), image: AssetImage(bg),
fit: BoxFit.fill, fit: BoxFit.fill,
opacity: bgOpacity,
), ),
), ),
child: Center( child: Center(
@@ -0,0 +1,282 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:p_tits_pas/services/auth_service.dart';
import 'package:p_tits_pas/widgets/image_button.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart';
/// Bandeau quotidien pastel : logo · pastilles Cahier de liaison / Agenda /
/// Contrat · menu user.
/// Réutilisable parent (#166) et AM (#169).
class QuotidienBandeau extends StatelessWidget {
final QuotidienNavSection selectedSection;
final ValueChanged<QuotidienNavSection> onSectionSelected;
final String userDisplayName;
final String? userEmail;
final VoidCallback? onProfileTap;
final VoidCallback? onSearchAmTap;
final VoidCallback? onSettingsTap;
final VoidCallback? onLogout;
const QuotidienBandeau({
super.key,
required this.selectedSection,
required this.onSectionSelected,
required this.userDisplayName,
this.userEmail,
this.onProfileTap,
this.onSearchAmTap,
this.onSettingsTap,
this.onLogout,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
child: Row(
children: [
Image.asset(
QuotidienTheme.logoAsset,
height: 44,
fit: BoxFit.contain,
),
const Spacer(),
_NavPills(
selected: selectedSection,
onSelected: onSectionSelected,
),
const Spacer(),
_UserMenu(
displayName: userDisplayName,
email: userEmail,
onProfileTap: onProfileTap,
onSearchAmTap: onSearchAmTap,
onSettingsTap: onSettingsTap,
onLogout: onLogout,
),
],
),
);
}
}
class _NavPills extends StatelessWidget {
final QuotidienNavSection selected;
final ValueChanged<QuotidienNavSection> onSelected;
const _NavPills({
required this.selected,
required this.onSelected,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
_pill(
label: 'Cahier de liaison',
section: QuotidienNavSection.liaison,
),
const SizedBox(width: 10),
_pill(
label: 'Agenda',
section: QuotidienNavSection.agenda,
),
const SizedBox(width: 10),
_pill(
label: 'Contrat',
section: QuotidienNavSection.contrat,
),
],
);
}
Widget _pill({
required String label,
required QuotidienNavSection section,
}) {
final active = selected == section;
// Même famille que l’inscription : fonds « dessinés » (pas Material).
// Les deux pastilles ont la même silhouette (ratio ~4:1) : la largeur
// est dérivée de la hauteur pour ne jamais déformer le PNG.
// Chaque section garde sa couleur ; l'inactive est juste plus claire.
return ImageButton(
bg: QuotidienTheme.pillAssetFor(section),
bgOpacity: active ? 1.0 : QuotidienTheme.pillInactiveOpacity,
width: _pillHeight * QuotidienTheme.pillAspectRatio,
height: _pillHeight,
text: label,
textColor: QuotidienTheme.ink,
fontSize: 14,
onPressed: () => onSelected(section),
);
}
static const double _pillHeight = 46;
}
class _UserMenu extends StatelessWidget {
final String displayName;
final String? email;
final VoidCallback? onProfileTap;
final VoidCallback? onSearchAmTap;
final VoidCallback? onSettingsTap;
final VoidCallback? onLogout;
const _UserMenu({
required this.displayName,
this.email,
this.onProfileTap,
this.onSearchAmTap,
this.onSettingsTap,
this.onLogout,
});
@override
Widget build(BuildContext context) {
final shortName = displayName.trim().isEmpty ? 'Compte' : displayName.trim();
const double pillHeight = 46;
const double pillWidth = pillHeight * QuotidienTheme.pillAspectRatio;
return PopupMenuButton<String>(
tooltip: 'Menu utilisateur',
offset: const Offset(0, pillHeight + 4),
color: QuotidienTheme.ivory,
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: QuotidienTheme.lavender.withOpacity(0.6)),
),
onSelected: (value) async {
switch (value) {
case 'profile':
onProfileTap?.call();
break;
case 'search_am':
onSearchAmTap?.call();
break;
case 'settings':
onSettingsTap?.call();
break;
case 'logout':
await _confirmLogout(context);
break;
}
},
itemBuilder: (context) => [
if (email != null && email!.trim().isNotEmpty)
PopupMenuItem(
enabled: false,
child: Text(
email!,
style: TextStyle(color: QuotidienTheme.muted, fontSize: 12),
),
),
const PopupMenuDivider(),
const PopupMenuItem(
value: 'profile',
child: ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.person_outline, size: 20),
title: Text('Profil'),
),
),
const PopupMenuItem(
value: 'search_am',
child: ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.search, size: 20),
title: Text('Recherche AM'),
),
),
const PopupMenuItem(
value: 'settings',
child: ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.settings_outlined, size: 20),
title: Text('Paramètres'),
),
),
const PopupMenuDivider(),
const PopupMenuItem(
value: 'logout',
child: ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.logout, size: 20),
title: Text('Déconnexion'),
),
),
],
// Même pastille « dessinée » que la nav, teinte violet pastel charte.
child: Container(
width: pillWidth,
height: pillHeight,
padding: const EdgeInsets.symmetric(horizontal: 14),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(QuotidienTheme.pillLavenderAsset),
fit: BoxFit.fill,
),
),
child: Row(
children: [
const Icon(
Icons.person_outline,
size: 20,
color: QuotidienTheme.ink,
),
const SizedBox(width: 6),
Expanded(
child: Text(
shortName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.merienda(
fontSize: 13,
fontWeight: FontWeight.bold,
color: QuotidienTheme.ink,
),
),
),
const Icon(
Icons.keyboard_arrow_down,
size: 18,
color: QuotidienTheme.ink,
),
],
),
),
);
}
Future<void> _confirmLogout(BuildContext context) async {
final ok = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Déconnexion'),
content: const Text('Voulez-vous vraiment vous déconnecter ?'),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
child: const Text('Annuler'),
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: const Text('Déconnexion'),
),
],
),
);
if (ok != true) return;
onLogout?.call();
await AuthService.logout();
if (context.mounted) {
context.go('/login');
}
}
}
@@ -0,0 +1,320 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:p_tits_pas/widgets/app_footer.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_bandeau.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart';
/// Coquille « Cahier de liaison » quotidien 3 colonnes + bandeau (#166).
/// L’AM (#169) réutilise ce widget en injectant ses slots.
class QuotidienShell extends StatelessWidget {
final QuotidienNavSection selectedSection;
final ValueChanged<QuotidienNavSection> onSectionSelected;
final String userDisplayName;
final String? userEmail;
final VoidCallback? onProfileTap;
final VoidCallback? onSearchAmTap;
final VoidCallback? onSettingsTap;
final VoidCallback? onLogout;
/// Colonne gauche (couple + cartes + actions).
final Widget leftColumn;
/// Colonne milieu (blog).
final Widget centerColumn;
/// Colonne droite (messagerie).
final Widget rightColumn;
/// Corps affiché hors Cahier de liaison (Agenda / Contrat stubs).
final Widget? agendaBody;
final Widget? contratBody;
const QuotidienShell({
super.key,
required this.selectedSection,
required this.onSectionSelected,
required this.userDisplayName,
required this.leftColumn,
required this.centerColumn,
required this.rightColumn,
this.userEmail,
this.onProfileTap,
this.onSearchAmTap,
this.onSettingsTap,
this.onLogout,
this.agendaBody,
this.contratBody,
});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: QuotidienTheme.ivory,
body: Container(
decoration: QuotidienTheme.paperBackground(),
child: Column(
children: [
QuotidienBandeau(
selectedSection: selectedSection,
onSectionSelected: onSectionSelected,
userDisplayName: userDisplayName,
userEmail: userEmail,
onProfileTap: onProfileTap,
onSearchAmTap: onSearchAmTap,
onSettingsTap: onSettingsTap,
onLogout: onLogout,
),
const QuotidienPencilDivider(),
Expanded(child: _bodyForSection(context)),
const QuotidienPencilDivider(),
const AppFooter(showTopBorder: false),
],
),
),
);
}
Widget _bodyForSection(BuildContext context) {
switch (selectedSection) {
case QuotidienNavSection.liaison:
return _ThreeColumns(
left: leftColumn,
center: centerColumn,
right: rightColumn,
);
case QuotidienNavSection.agenda:
return agendaBody ??
const QuotidienStubPage(
title: 'Agenda',
message: 'Page Agenda — à venir.',
);
case QuotidienNavSection.contrat:
return contratBody ??
const QuotidienStubPage(
title: 'Contrat',
message: 'Page Contrat — à venir.',
);
}
}
}
/// Trait « crayon gris » dessiné à la main : sépare bandeau / corps / footer
/// (horizontal) et les 3 colonnes (vertical). Le PNG (2400×28) est étiré dans
/// le sens du trait seulement : sur un trait, c'est invisible.
class QuotidienPencilDivider extends StatelessWidget {
final Axis axis;
/// Épaisseur de la zone du trait (hauteur si horizontal, largeur sinon).
final double thickness;
final EdgeInsets padding;
const QuotidienPencilDivider({
super.key,
this.axis = Axis.horizontal,
this.thickness = 14,
this.padding = const EdgeInsets.symmetric(horizontal: 24),
});
const QuotidienPencilDivider.vertical({
super.key,
this.thickness = 14,
this.padding = const EdgeInsets.symmetric(vertical: 16),
}) : axis = Axis.vertical;
@override
Widget build(BuildContext context) {
final horizontal = axis == Axis.horizontal;
return Padding(
padding: padding,
child: SizedBox(
width: horizontal ? double.infinity : thickness,
height: horizontal ? thickness : double.infinity,
child: Image.asset(
horizontal
? QuotidienTheme.pencilLineAsset
: QuotidienTheme.pencilLineVerticalAsset,
fit: BoxFit.fill,
filterQuality: FilterQuality.medium,
),
),
);
}
}
class _ThreeColumns extends StatelessWidget {
final Widget left;
final Widget center;
final Widget right;
const _ThreeColumns({
required this.left,
required this.center,
required this.right,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final wide = constraints.maxWidth >= 900;
if (!wide) {
// Socle mobile temporaire (#188 = swipe dédié) : pile verticale.
return ListView(
padding: const EdgeInsets.fromLTRB(12, 4, 12, 12),
children: [
_ColumnPanel(child: left),
const QuotidienPencilDivider(
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 4),
),
_ColumnPanel(child: center),
const QuotidienPencilDivider(
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 4),
),
_ColumnPanel(child: right),
],
);
}
return Padding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: _ColumnPanel(child: left)),
const QuotidienPencilDivider.vertical(),
Expanded(child: _ColumnPanel(child: center)),
const QuotidienPencilDivider.vertical(),
Expanded(child: _ColumnPanel(child: right)),
],
),
);
},
);
}
}
/// Panneau colonne (carte pastel semi-transparente).
class _ColumnPanel extends StatelessWidget {
final Widget child;
const _ColumnPanel({required this.child});
@override
Widget build(BuildContext context) {
return Container(
// Pas de bordure Material : la séparation est assurée par les traits
// crayon, on garde juste un léger fond.
decoration: BoxDecoration(
color: QuotidienTheme.columnCardFill.withOpacity(0.82),
borderRadius: BorderRadius.circular(16),
),
clipBehavior: Clip.antiAlias,
child: child,
);
}
}
/// Placeholder de colonne métier (branchable par tickets C/D/E).
class QuotidienColumnPlaceholder extends StatelessWidget {
final String title;
final String subtitle;
final IconData icon;
const QuotidienColumnPlaceholder({
super.key,
required this.title,
required this.subtitle,
required this.icon,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Icon(icon, color: QuotidienTheme.lavender, size: 22),
const SizedBox(width: 8),
Text(
title,
style: GoogleFonts.merienda(
fontSize: 18,
fontWeight: FontWeight.w600,
color: QuotidienTheme.ink,
),
),
],
),
const SizedBox(height: 12),
Expanded(
child: Center(
child: Text(
subtitle,
textAlign: TextAlign.center,
style: GoogleFonts.merriweather(
fontSize: 13,
color: QuotidienTheme.muted,
height: 1.4,
),
),
),
),
],
),
);
}
}
/// Stub pleine page (Agenda / Contrat) — contenu riche hors #166.
class QuotidienStubPage extends StatelessWidget {
final String title;
final String message;
const QuotidienStubPage({
super.key,
required this.title,
required this.message,
});
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
child: Container(
margin: const EdgeInsets.all(24),
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.85),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey.shade300),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: GoogleFonts.merienda(
fontSize: 22,
fontWeight: FontWeight.w600,
color: QuotidienTheme.ink,
),
),
const SizedBox(height: 12),
Text(
message,
textAlign: TextAlign.center,
style: GoogleFonts.merriweather(
fontSize: 14,
color: QuotidienTheme.muted,
),
),
],
),
),
),
);
}
}
@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
/// Couleurs / tokens du quotidien parent–AM (#166) — lignée papier / pastel.
/// Pas le violet Material du dashboard staff.
abstract final class QuotidienTheme {
static const Color ink = Color(0xFF2F2F2F);
static const Color ivory = Color(0xFFFFFEF9);
static const Color turquoise = Color(0xFF8AD0C8);
static const Color lavender = Color(0xFFC6A3D8);
static const Color coral = Color(0xFFF4A28C);
static const Color softGreenPill = Color(0xFFB8D9A8);
static const Color columnCardFill = Color(0xFFF7F3EA);
static const Color muted = Color(0xFF6B6B6B);
static const String paperAsset = 'assets/images/paper2.png';
static const String logoAsset = 'assets/images/logo.png';
/// Pastilles « dessinées » du bandeau : même silhouette (1190×299), une
/// couleur charte par section ; l'inactive est la même, plus transparente.
static const double pillInactiveOpacity = 0.45;
static const String pillIvoryAsset = 'assets/images/bg_ivoire_pill.png';
static const String pillYellowAsset = 'assets/images/bg_yellow_pill.png';
static const String pillPeachAsset = 'assets/images/bg_peach_pill.png';
static const String pillTurquoiseAsset =
'assets/images/bg_turquoise_pill.png';
static const String pillLavenderAsset =
'assets/images/bg_lavender_pill.png';
/// Trait crayon gris (2400×28, transparent) : séparateurs bandeau / corps /
/// footer. Version verticale (28×2400) entre les colonnes.
static const String pencilLineAsset = 'assets/images/pencil_line_grey.png';
static const String pencilLineVerticalAsset =
'assets/images/pencil_line_grey_v.png';
static String pillAssetFor(QuotidienNavSection section) {
switch (section) {
case QuotidienNavSection.liaison:
return pillYellowAsset;
case QuotidienNavSection.agenda:
return pillPeachAsset;
case QuotidienNavSection.contrat:
return pillTurquoiseAsset;
}
}
/// Largeur / hauteur des PNG de pastille, à respecter pour ne pas déformer.
static const double pillAspectRatio = 1190 / 298;
static BoxDecoration paperBackground() {
return const BoxDecoration(
image: DecorationImage(
image: AssetImage(paperAsset),
fit: BoxFit.cover,
repeat: ImageRepeat.repeat,
),
);
}
}
/// Sections du bandeau quotidien (Cahier de liaison / Agenda / Contrat).
/// `liaison` = le hub « Cahier de liaison » (cartes · blog · messagerie).
enum QuotidienNavSection {
liaison,
agenda,
contrat,
}