Merge origin/develop: résolution conflits doc tickets
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,6 +19,8 @@ import '../screens/auth/am_register_step2_screen.dart';
|
||||
import '../screens/auth/am_register_step3_screen.dart';
|
||||
import '../screens/auth/am_register_step4_screen.dart';
|
||||
import '../screens/home/home_screen.dart';
|
||||
import '../screens/administrateurs/admin_dashboardScreen.dart';
|
||||
import '../screens/home/parent_screen/ParentDashboardScreen.dart';
|
||||
import '../screens/unknown_screen.dart';
|
||||
|
||||
// --- Provider Instances ---
|
||||
@@ -47,6 +49,18 @@ class AppRouter {
|
||||
path: '/home',
|
||||
builder: (BuildContext context, GoRouterState state) => const HomeScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/admin-dashboard',
|
||||
builder: (BuildContext context, GoRouterState state) => const AdminDashboardScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/parent-dashboard',
|
||||
builder: (BuildContext context, GoRouterState state) => const ParentDashboardScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/am-dashboard',
|
||||
builder: (BuildContext context, GoRouterState state) => const HomeScreen(),
|
||||
),
|
||||
|
||||
// --- Parent Registration Flow ---
|
||||
ShellRoute(
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class NannyRegistrationData extends ChangeNotifier {
|
||||
// Step 1: Identity Info
|
||||
String firstName = '';
|
||||
String lastName = '';
|
||||
String streetAddress = ''; // Nouveau pour N° et Rue
|
||||
String postalCode = ''; // Nouveau
|
||||
String city = ''; // Nouveau
|
||||
String phone = '';
|
||||
String email = '';
|
||||
String password = '';
|
||||
// String? photoPath; // Déplacé ou géré à l'étape 2
|
||||
// bool photoConsent = false; // Déplacé ou géré à l'étape 2
|
||||
|
||||
// Step 2: Professional Info
|
||||
String? photoPath; // Ajouté pour l'étape 2
|
||||
bool photoConsent = false; // Ajouté pour l'étape 2
|
||||
DateTime? dateOfBirth;
|
||||
String birthCity = ''; // Nouveau
|
||||
String birthCountry = ''; // Nouveau
|
||||
// String placeOfBirth = ''; // Remplacé par birthCity et birthCountry
|
||||
String nir = ''; // Numéro de Sécurité Sociale
|
||||
String agrementNumber = ''; // Numéro d'agrément
|
||||
int? capacity; // Number of children the nanny can look after
|
||||
|
||||
// Step 3: Presentation & CGU
|
||||
String presentationText = '';
|
||||
bool cguAccepted = false;
|
||||
|
||||
// --- Methods to update data and notify listeners ---
|
||||
|
||||
void updateIdentityInfo({
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
String? streetAddress, // Modifié
|
||||
String? postalCode, // Nouveau
|
||||
String? city, // Nouveau
|
||||
String? phone,
|
||||
String? email,
|
||||
String? password,
|
||||
}) {
|
||||
this.firstName = firstName ?? this.firstName;
|
||||
this.lastName = lastName ?? this.lastName;
|
||||
this.streetAddress = streetAddress ?? this.streetAddress; // Modifié
|
||||
this.postalCode = postalCode ?? this.postalCode; // Nouveau
|
||||
this.city = city ?? this.city; // Nouveau
|
||||
this.phone = phone ?? this.phone;
|
||||
this.email = email ?? this.email;
|
||||
this.password = password ?? this.password;
|
||||
// if (photoPath != null || this.photoPath != null) { // Supprimé de l'étape 1
|
||||
// this.photoPath = photoPath;
|
||||
// }
|
||||
// this.photoConsent = photoConsent ?? this.photoConsent; // Supprimé de l'étape 1
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void updateProfessionalInfo({
|
||||
String? photoPath,
|
||||
bool? photoConsent,
|
||||
DateTime? dateOfBirth,
|
||||
String? birthCity, // Nouveau
|
||||
String? birthCountry, // Nouveau
|
||||
// String? placeOfBirth, // Remplacé
|
||||
String? nir,
|
||||
String? agrementNumber,
|
||||
int? capacity,
|
||||
}) {
|
||||
// Allow setting photoPath to null explicitly
|
||||
if (photoPath != null || this.photoPath != null) {
|
||||
this.photoPath = photoPath;
|
||||
}
|
||||
this.photoConsent = photoConsent ?? this.photoConsent;
|
||||
this.dateOfBirth = dateOfBirth ?? this.dateOfBirth;
|
||||
this.birthCity = birthCity ?? this.birthCity; // Nouveau
|
||||
this.birthCountry = birthCountry ?? this.birthCountry; // Nouveau
|
||||
// this.placeOfBirth = placeOfBirth ?? this.placeOfBirth; // Remplacé
|
||||
this.nir = nir ?? this.nir;
|
||||
this.agrementNumber = agrementNumber ?? this.agrementNumber;
|
||||
this.capacity = capacity ?? this.capacity;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void updatePresentationAndCgu({
|
||||
String? presentationText,
|
||||
bool? cguAccepted,
|
||||
}) {
|
||||
this.presentationText = presentationText ?? this.presentationText;
|
||||
this.cguAccepted = cguAccepted ?? this.cguAccepted;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// --- Getters for validation or display ---
|
||||
bool get isStep1Complete =>
|
||||
firstName.isNotEmpty &&
|
||||
lastName.isNotEmpty &&
|
||||
streetAddress.isNotEmpty && // Modifié
|
||||
postalCode.isNotEmpty && // Nouveau
|
||||
city.isNotEmpty && // Nouveau
|
||||
phone.isNotEmpty &&
|
||||
email.isNotEmpty &&
|
||||
password.isNotEmpty;
|
||||
|
||||
bool get isStep2Complete =>
|
||||
// photoConsent is mandatory if a photo is system-required, otherwise optional.
|
||||
// For now, let's assume if photoPath is present, consent should ideally be true.
|
||||
// Or, make consent always mandatory if photo section exists.
|
||||
// Based on new mockup, photo is present, so consent might be implicitly or explicitly needed.
|
||||
(photoPath != null ? photoConsent == true : true) && // Ajuster selon la logique de consentement désirée
|
||||
dateOfBirth != null &&
|
||||
birthCity.isNotEmpty &&
|
||||
birthCountry.isNotEmpty &&
|
||||
nir.isNotEmpty && // Basic check, could add validation
|
||||
agrementNumber.isNotEmpty &&
|
||||
capacity != null && capacity! > 0;
|
||||
|
||||
bool get isStep3Complete =>
|
||||
// presentationText is optional as per CDC (message au gestionnaire)
|
||||
cguAccepted;
|
||||
|
||||
bool get isRegistrationComplete =>
|
||||
isStep1Complete && isStep2Complete && isStep3Complete;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NannyRegistrationData('
|
||||
'firstName: $firstName, lastName: $lastName, '
|
||||
'streetAddress: $streetAddress, postalCode: $postalCode, city: $city, '
|
||||
'phone: $phone, email: $email, '
|
||||
// 'photoPath: $photoPath, photoConsent: $photoConsent, ' // Commenté car déplacé/modifié
|
||||
'dateOfBirth: $dateOfBirth, birthCity: $birthCity, birthCountry: $birthCountry, '
|
||||
'nir: $nir, agrementNumber: $agrementNumber, capacity: $capacity, '
|
||||
'photoPath (step2): $photoPath, photoConsent (step2): $photoConsent, '
|
||||
'presentationText: $presentationText, cguAccepted: $cguAccepted)';
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,12 @@ class AppUser {
|
||||
id: json['id'] as String,
|
||||
email: json['email'] as String,
|
||||
role: json['role'] as String,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
createdAt: json['createdAt'] != null
|
||||
? DateTime.parse(json['createdAt'] as String)
|
||||
: DateTime.now(),
|
||||
updatedAt: json['updatedAt'] != null
|
||||
? DateTime.parse(json['updatedAt'] as String)
|
||||
: DateTime.now(),
|
||||
changementMdpObligatoire: json['changement_mdp_obligatoire'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,21 +116,23 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
/// Redirige l'utilisateur selon son rôle
|
||||
/// Redirige l'utilisateur selon son rôle (GoRouter : context.go).
|
||||
void _redirectUserByRole(String role) {
|
||||
setState(() => _isLoading = false);
|
||||
switch (role.toLowerCase()) {
|
||||
case 'super_admin':
|
||||
case 'administrateur':
|
||||
case 'gestionnaire':
|
||||
Navigator.pushReplacementNamed(context, '/admin-dashboard');
|
||||
context.go('/admin-dashboard');
|
||||
break;
|
||||
case 'parent':
|
||||
Navigator.pushReplacementNamed(context, '/parent-dashboard');
|
||||
context.go('/parent-dashboard');
|
||||
break;
|
||||
case 'assistante_maternelle':
|
||||
Navigator.pushReplacementNamed(context, '/am-dashboard');
|
||||
context.go('/am-dashboard');
|
||||
break;
|
||||
default:
|
||||
Navigator.pushReplacementNamed(context, '/home');
|
||||
context.go('/home');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class NannyRegisterConfirmationScreen extends StatelessWidget {
|
||||
const NannyRegisterConfirmationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Inscription Soumise'),
|
||||
automaticallyImplyLeading: false, // Remove back button
|
||||
),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.check_circle_outline, color: Colors.green, size: 80),
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
'Votre demande d\'inscription a été soumise avec succès !',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Text(
|
||||
'Votre compte est en attente de validation par un gestionnaire. Vous recevrez une notification par e-mail une fois votre compte activé.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// Navigate back to the login screen
|
||||
context.go('/login');
|
||||
},
|
||||
child: const Text('Retour à la connexion'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,15 @@ class AuthService {
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final data = jsonDecode(response.body);
|
||||
|
||||
// Stocker les tokens
|
||||
await TokenService.saveToken(data['accessToken']);
|
||||
await TokenService.saveRefreshToken(data['refreshToken']);
|
||||
|
||||
// Récupérer le profil utilisateur pour avoir toutes les infos
|
||||
final user = await _fetchUserProfile(data['accessToken']);
|
||||
// API renvoie access_token / refresh_token (snake_case)
|
||||
final accessToken = data['access_token'] as String? ?? data['accessToken'] as String?;
|
||||
final refreshToken = data['refresh_token'] as String? ?? data['refreshToken'] as String?;
|
||||
if (accessToken == null) throw Exception('Token absent dans la réponse serveur');
|
||||
|
||||
await TokenService.saveToken(accessToken);
|
||||
await TokenService.saveRefreshToken(refreshToken ?? '');
|
||||
|
||||
final user = await _fetchUserProfile(accessToken);
|
||||
|
||||
// Stocker l'utilisateur en cache
|
||||
await _saveCurrentUser(user);
|
||||
@@ -80,8 +82,9 @@ class AuthService {
|
||||
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.changePasswordRequired}'),
|
||||
headers: ApiConfig.authHeaders(token),
|
||||
body: jsonEncode({
|
||||
'currentPassword': currentPassword,
|
||||
'newPassword': newPassword,
|
||||
'mot_de_passe_actuel': currentPassword,
|
||||
'nouveau_mot_de_passe': newPassword,
|
||||
'confirmation_mot_de_passe': newPassword,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -55,8 +55,14 @@ class ChoiceCardWidget extends StatelessWidget {
|
||||
required bool isMobile,
|
||||
}) {
|
||||
final Color baseRoseColor = Colors.pink.shade300;
|
||||
final Color initialShadow = baseRoseColor.withAlpha(90);
|
||||
final Color hoverShadow = baseRoseColor.withAlpha(130);
|
||||
final Color initialShadow = isMobile
|
||||
? Colors.black.withOpacity(0.45)
|
||||
: baseRoseColor.withAlpha(90);
|
||||
final Color hoverShadow = isMobile
|
||||
? Colors.black.withOpacity(0.5)
|
||||
: baseRoseColor.withAlpha(130);
|
||||
final double initialElevation = isMobile ? 14.0 : 4.0;
|
||||
final double hoverElevation = isMobile ? 18.0 : 8.0;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -64,6 +70,8 @@ class ChoiceCardWidget extends StatelessWidget {
|
||||
HoverReliefWidget(
|
||||
onPressed: onPressed,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
initialElevation: initialElevation,
|
||||
hoverElevation: hoverElevation,
|
||||
initialShadowColor: initialShadow,
|
||||
hoverShadowColor: hoverShadow,
|
||||
child: Padding(
|
||||
|
||||
Reference in New Issue
Block a user