diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1540ca9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,18 @@ +# Fins de ligne : toujours LF dans le dépôt (évite les conflits Linux/Windows) +* text=auto eol=lf + +# Fichiers binaires : pas de conversion +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.webp binary +*.pdf binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary + +# Scripts shell : toujours LF +*.sh text eol=lf diff --git a/check_hash.js b/check_hash.js new file mode 100644 index 0000000..cb8e7d6 --- /dev/null +++ b/check_hash.js @@ -0,0 +1,7 @@ +const bcrypt = require('bcrypt'); + +const pass = '!Bezons2014'; + +bcrypt.hash(pass, 10).then(hash => { + console.log('New Hash:', hash); +}).catch(err => console.error(err)); diff --git a/docs/23_LISTE-TICKETS.md b/docs/23_LISTE-TICKETS.md index 46e1f90..5a879ac 100644 --- a/docs/23_LISTE-TICKETS.md +++ b/docs/23_LISTE-TICKETS.md @@ -840,7 +840,7 @@ Créer l'écran de création de mot de passe (lien reçu par email). --- -### Ticket #44 : [Frontend] Dashboard Gestionnaire - Structure +### Ticket #44 : [Frontend] Dashboard Gestionnaire - Structure ✅ **Estimation** : 2h **Labels** : `frontend`, `p3`, `gestionnaire` @@ -848,9 +848,10 @@ Créer l'écran de création de mot de passe (lien reçu par email). Créer la structure du dashboard gestionnaire avec 2 onglets. **Tâches** : -- [ ] Layout avec 2 onglets (Parents / AM) -- [ ] Navigation entre onglets -- [ ] État vide ("Aucune demande") +- [x] Dashboard gestionnaire = même shell que admin (sans onglet Paramètres), libellé « Gestionnaire » +- [x] Réutilisation du widget UserManagementPanel (ex-AdminUserManagementPanel) avec 3 onglets (Gestionnaires, Parents, Assistantes maternelles) ; onglet Administrateurs masqué +- [x] Redirection login rôle `gestionnaire` vers `/gestionnaire-dashboard` +- [ ] État vide dédié ("Aucune demande") — optionnel, contenu actuel = listes existantes --- diff --git a/docs/POINT_TICKETS_FRONT_API.txt b/docs/POINT_TICKETS_FRONT_API.txt index f78be3d..7f32219 100644 --- a/docs/POINT_TICKETS_FRONT_API.txt +++ b/docs/POINT_TICKETS_FRONT_API.txt @@ -14,7 +14,7 @@ Num | Etat | Titre 41 | closed | [Frontend] Inscription AM - Panneau 2 (Infos pro) 42 | closed | [Frontend] Inscription AM - Finalisation 43 | open | [Frontend] Écran Création Mot de Passe - 44 | open | [Frontend] Dashboard Gestionnaire - Structure + 44 | closed | [Frontend] Dashboard Gestionnaire - Structure 45 | open | [Frontend] Dashboard Gestionnaire - Liste Parents 46 | open | [Frontend] Dashboard Gestionnaire - Liste AM 47 | open | [Frontend] Écran Changement MDP Obligatoire diff --git a/frontend/lib/screens/auth/am_register_step1_screen.dart b/frontend/lib/screens/auth/am_register_step1_screen.dart index 06a8fa2..14a4db1 100644 --- a/frontend/lib/screens/auth/am_register_step1_screen.dart +++ b/frontend/lib/screens/auth/am_register_step1_screen.dart @@ -3,7 +3,6 @@ import 'package:provider/provider.dart'; import 'package:go_router/go_router.dart'; import '../../models/am_registration_data.dart'; -import '../../utils/data_generator.dart'; import '../../widgets/personal_info_form_screen.dart'; import '../../models/card_assets.dart'; @@ -14,19 +13,17 @@ class AmRegisterStep1Screen extends StatelessWidget { Widget build(BuildContext context) { final registrationData = Provider.of(context, listen: false); - // Générer des données de test si vide + // Données de test : Marie DUBOIS (jeu de test 03_seed_test_data.sql / docs/test-data) PersonalInfoData initialData; if (registrationData.firstName.isEmpty) { - final genFirstName = DataGenerator.firstName(); - final genLastName = DataGenerator.lastName(); initialData = PersonalInfoData( - firstName: genFirstName, - lastName: genLastName, - phone: DataGenerator.phone(), - email: DataGenerator.email(genFirstName, genLastName), - address: DataGenerator.address(), - postalCode: DataGenerator.postalCode(), - city: DataGenerator.city(), + firstName: 'Marie', + lastName: 'DUBOIS', + phone: '0696345678', + email: 'marie.dubois@ptits-pas.fr', + address: '25 Rue de la République', + postalCode: '95870', + city: 'Bezons', ); } else { initialData = PersonalInfoData( diff --git a/frontend/lib/screens/auth/am_register_step2_screen.dart b/frontend/lib/screens/auth/am_register_step2_screen.dart index 447280a..bf354d4 100644 --- a/frontend/lib/screens/auth/am_register_step2_screen.dart +++ b/frontend/lib/screens/auth/am_register_step2_screen.dart @@ -6,7 +6,6 @@ import 'dart:io'; import '../../models/am_registration_data.dart'; import '../../models/card_assets.dart'; -import '../../utils/data_generator.dart'; import '../../widgets/professional_info_form_screen.dart'; class AmRegisterStep2Screen extends StatefulWidget { @@ -54,17 +53,17 @@ class _AmRegisterStep2ScreenState extends State { capacity: registrationData.capacity, ); - // Générer des données de test si les champs sont vides (NIR = Marie Dubois du seed, Corse 2A) + // Données de test : Marie DUBOIS (jeu de test 03_seed_test_data.sql / docs/test-data) if (registrationData.dateOfBirth == null && registrationData.nir.isEmpty) { initialData = ProfessionalInfoData( photoPath: 'assets/images/icon_assmat.png', photoConsent: true, dateOfBirth: DateTime(1980, 6, 8), - birthCity: 'Ajaccio', + birthCity: 'Bezons', birthCountry: 'France', nir: '280062A00100191', - agrementNumber: 'AM${DataGenerator.randomIntInRange(10000, 100000)}', - capacity: DataGenerator.randomIntInRange(1, 5), + agrementNumber: 'AGR-2019-095001', + capacity: 4, ); } diff --git a/frontend/lib/screens/auth/am_register_step3_screen.dart b/frontend/lib/screens/auth/am_register_step3_screen.dart index 1fff3cb..7bda43f 100644 --- a/frontend/lib/screens/auth/am_register_step3_screen.dart +++ b/frontend/lib/screens/auth/am_register_step3_screen.dart @@ -13,12 +13,12 @@ class AmRegisterStep3Screen extends StatelessWidget { Widget build(BuildContext context) { final data = Provider.of(context, listen: false); - // Générer un texte de test si vide + // Données de test : Marie DUBOIS (jeu de test 03_seed_test_data.sql / docs/test-data) String initialText = data.presentationText; bool initialCgu = data.cguAccepted; if (initialText.isEmpty) { - initialText = 'Disponible immédiatement, plus de 10 ans d\'expérience avec les tout-petits. Formation aux premiers secours à jour. Je dispose d\'un jardin sécurisé et d\'un espace de jeu adapté.'; + initialText = 'Assistante maternelle agréée depuis 2019. Spécialité bébés 0-18 mois. Accueil bienveillant et cadre sécurisant. 2 places disponibles.'; initialCgu = true; } diff --git a/frontend/lib/widgets/custom_app_text_field.dart b/frontend/lib/widgets/custom_app_text_field.dart index 7471c69..967865a 100644 --- a/frontend/lib/widgets/custom_app_text_field.dart +++ b/frontend/lib/widgets/custom_app_text_field.dart @@ -56,6 +56,7 @@ class CustomAppTextField extends StatefulWidget { this.autofillHints, this.textInputAction, this.onFieldSubmitted, + this.inputFormatters, }); @override diff --git a/scripts/README-create-issue.md b/scripts/README-create-issue.md new file mode 100644 index 0000000..51fd65d --- /dev/null +++ b/scripts/README-create-issue.md @@ -0,0 +1,19 @@ +# Créer l’issue #84 (correctifs modale MDP) via l’API Gitea + +1. Définir un token valide : + `export GITEA_TOKEN="votre_token"` + ou créer `.gitea-token` à la racine du projet avec le token seul. + +2. Créer l’issue : + ```bash + cd /chemin/vers/PetitsPas + curl -s -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d @scripts/issue-84-payload.json \ + "https://git.ptits-pas.fr/api/v1/repos/jmartin/petitspas/issues" + ``` + +3. En cas de succès (HTTP 201), la réponse JSON contient le numéro de l’issue créée. + +Payload utilisé : `scripts/issue-84-payload.json` (titre + corps depuis `scripts/issue-84-body.txt`). diff --git a/scripts/create-gitea-issue.sh b/scripts/create-gitea-issue.sh new file mode 100644 index 0000000..3315612 --- /dev/null +++ b/scripts/create-gitea-issue.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Crée une issue Gitea via l'API. +# Usage: GITEA_TOKEN=xxx ./scripts/create-gitea-issue.sh +# Ou: mettre le token dans .gitea-token à la racine du projet. + +set -e +BASE_URL="${GITEA_URL:-https://git.ptits-pas.fr/api/v1}" +REPO="jmartin/petitspas" + +if [ -z "$GITEA_TOKEN" ]; then + if [ -f .gitea-token ]; then + GITEA_TOKEN=$(cat .gitea-token) + fi +fi + +if [ -z "$GITEA_TOKEN" ]; then + echo "Définir GITEA_TOKEN ou créer .gitea-token avec votre token Gitea." + exit 1 +fi + +TITLE="$1" +BODY="$2" +if [ -z "$TITLE" ]; then + echo "Usage: $0 \"Titre de l'issue\" \"Corps (optionnel)\"" + exit 1 +fi + +# Build JSON (escape body for JSON) +BODY_ESC=$(echo "$BODY" | jq -Rs . 2>/dev/null || echo "null") +if [ "$BODY_ESC" = "null" ] || [ -z "$BODY" ]; then + PAYLOAD=$(jq -n --arg t "$TITLE" '{title: $t}') +else + PAYLOAD=$(jq -n --arg t "$TITLE" --arg b "$BODY" '{title: $t, body: $b}') +fi + +RESP=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$PAYLOAD" \ + "$BASE_URL/repos/$REPO/issues") +HTTP_CODE=$(echo "$RESP" | tail -1) +BODY_RESP=$(echo "$RESP" | sed '$d') + +if [ "$HTTP_CODE" = "201" ]; then + ISSUE_NUM=$(echo "$BODY_RESP" | jq -r .number) + echo "Issue #$ISSUE_NUM créée." + echo "$BODY_RESP" | jq . +else + echo "Erreur HTTP $HTTP_CODE: $BODY_RESP" + exit 1 +fi diff --git a/scripts/gitea-close-issue-with-comment.sh b/scripts/gitea-close-issue-with-comment.sh new file mode 100644 index 0000000..ad545b8 --- /dev/null +++ b/scripts/gitea-close-issue-with-comment.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Poste un commentaire sur une issue Gitea puis la ferme. +# Usage: GITEA_TOKEN=xxx ./scripts/gitea-close-issue-with-comment.sh "Commentaire" +# Ou: mettre le token dans .gitea-token à la racine du projet. +# Exemple: ./scripts/gitea-close-issue-with-comment.sh 15 "Livré : panneau Paramètres opérationnel." + +set -e +ISSUE="${1:?Usage: $0 \"Commentaire\"}" +COMMENT="${2:?Usage: $0 \"Commentaire\"}" +BASE_URL="${GITEA_URL:-https://git.ptits-pas.fr/api/v1}" +REPO="jmartin/petitspas" + +if [ -z "$GITEA_TOKEN" ]; then + if [ -f .gitea-token ]; then + GITEA_TOKEN=$(cat .gitea-token) + fi +fi + +if [ -z "$GITEA_TOKEN" ]; then + echo "Définir GITEA_TOKEN ou créer .gitea-token avec votre token Gitea." + exit 1 +fi + +# 1) Poster le commentaire +echo "Ajout du commentaire sur l'issue #$ISSUE..." +# Échapper pour JSON (guillemets et backslash) +COMMENT_ESC=$(printf '%s' "$COMMENT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\r//g') +PAYLOAD="{\"body\":\"$COMMENT_ESC\"}" +RESP=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$PAYLOAD" \ + "$BASE_URL/repos/$REPO/issues/$ISSUE/comments") +HTTP_CODE=$(echo "$RESP" | tail -1) +BODY=$(echo "$RESP" | sed '$d') + +if [ "$HTTP_CODE" != "201" ]; then + echo "Erreur HTTP $HTTP_CODE lors du commentaire: $BODY" + exit 1 +fi +echo "Commentaire ajouté." + +# 2) Fermer l'issue +echo "Fermeture de l'issue #$ISSUE..." +RESP2=$(curl -s -w "\n%{http_code}" -X PATCH \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"state":"closed"}' \ + "$BASE_URL/repos/$REPO/issues/$ISSUE") +HTTP_CODE2=$(echo "$RESP2" | tail -1) +BODY2=$(echo "$RESP2" | sed '$d') + +if [ "$HTTP_CODE2" = "200" ] || [ "$HTTP_CODE2" = "201" ]; then + echo "Issue #$ISSUE fermée." +else + echo "Erreur HTTP $HTTP_CODE2: $BODY2" + exit 1 +fi diff --git a/scripts/issue-84-body.txt b/scripts/issue-84-body.txt new file mode 100644 index 0000000..770e6f3 --- /dev/null +++ b/scripts/issue-84-body.txt @@ -0,0 +1,14 @@ +Correctifs et améliorations de la modale de changement de mot de passe obligatoire affichée à la première connexion admin. + +**Périmètre :** +- Ajustements visuels / UX de la modale (ChangePasswordDialog) +- Cohérence charte graphique, espacements, lisibilité +- Comportement (validation, messages d'erreur, fermeture) +- Lien de test en debug sur l'écran login (« Test modale MDP ») pour faciliter les réglages + +**Tâches :** +- [ ] Revoir le design de la modale (relief, bordures, couleurs) +- [ ] Vérifier les champs (MDP actuel, nouveau, confirmation) et validations +- [ ] Ajuster les textes et messages d'erreur +- [ ] Tester sur mobile et desktop +- [ ] Retirer ou conditionner le lien « Test modale MDP » en production si besoin diff --git a/scripts/issue-84-payload.json b/scripts/issue-84-payload.json new file mode 100644 index 0000000..731ad3d --- /dev/null +++ b/scripts/issue-84-payload.json @@ -0,0 +1 @@ +{"title": "[Frontend] Bug – Correctifs modale Changement MDP (première connexion admin)", "body": "Correctifs et améliorations de la modale de changement de mot de passe obligatoire affichée à la première connexion admin.\n\n**Périmètre :**\n- Ajustements visuels / UX de la modale (ChangePasswordDialog)\n- Cohérence charte graphique, espacements, lisibilité\n- Comportement (validation, messages d'erreur, fermeture)\n- Lien de test en debug sur l'écran login (« Test modale MDP ») pour faciliter les réglages\n\n**Tâches :**\n- [ ] Revoir le design de la modale (relief, bordures, couleurs)\n- [ ] Vérifier les champs (MDP actuel, nouveau, confirmation) et validations\n- [ ] Ajuster les textes et messages d'erreur\n- [ ] Tester sur mobile et desktop\n- [ ] Retirer ou conditionner le lien « Test modale MDP » en production si besoin\n"} \ No newline at end of file