Merge pull request 'feat: Implement authentication service and login handling with role-based navigation' (#64) from feature/FRONT-07 into dev

Reviewed-on: #64
This commit is contained in:
hmoussa 2025-09-03 09:05:58 +00:00
commit c6f9fd3be7
10 changed files with 242 additions and 11 deletions

View File

@ -20,6 +20,11 @@ public final class GeneratedPluginRegistrant {
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Error registering plugin flutter_plugin_android_lifecycle, io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin", e); Log.e(TAG, "Error registering plugin flutter_plugin_android_lifecycle, io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin", e);
} }
try {
flutterEngine.getPlugins().add(new com.it_nomads.fluttersecurestorage.FlutterSecureStoragePlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin flutter_secure_storage, com.it_nomads.fluttersecurestorage.FlutterSecureStoragePlugin", e);
}
try { try {
flutterEngine.getPlugins().add(new io.flutter.plugins.imagepicker.ImagePickerPlugin()); flutterEngine.getPlugins().add(new io.flutter.plugins.imagepicker.ImagePickerPlugin());
} catch (Exception e) { } catch (Exception e) {

View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
class GestionnairesCreate extends StatelessWidget {
const GestionnairesCreate({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Créer un gestionnaire'),
),
body: const Center(
child: Text('Formulaire de création de gestionnaire'),
),
);
}
}

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:p_tits_pas/services/auth_service.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:p_tits_pas/services/bug_report_service.dart'; import 'package:p_tits_pas/services/bug_report_service.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -19,6 +20,7 @@ class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController(); final _emailController = TextEditingController();
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
final AuthService _authService = AuthService();
@override @override
void dispose() { void dispose() {
@ -47,6 +49,46 @@ class _LoginPageState extends State<LoginPage> {
return null; return null;
} }
Future<void> _handleLogin() async {
if (_formKey.currentState?.validate() ?? false) {
try {
final response = await _authService.login(
_emailController.text,
_passwordController.text,
);
if (!mounted) return;
// Navigation selon le rôle
switch (response.role.toLowerCase()) {
case 'parent':
Navigator.pushReplacementNamed(context, '/parent-dashboard');
break;
case 'assistante_maternelle':
Navigator.pushReplacementNamed(context, '/assistante_maternelle_dashboard');
break;
case 'admin':
Navigator.pushReplacementNamed(context, '/admin_dashboard');
break;
case 'gestionnaire':
Navigator.pushReplacementNamed(context, '/gestionnaire_dashboard');
break;
default:
Navigator.pushReplacementNamed(context, '/home');
}
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Échec de la connexion. Vérifiez vos identifiants.'),
backgroundColor: Colors.red,
),
);
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -144,11 +186,7 @@ class _LoginPageState extends State<LoginPage> {
height: 40, height: 40,
text: 'Se connecter', text: 'Se connecter',
textColor: const Color(0xFF2D6A4F), textColor: const Color(0xFF2D6A4F),
onPressed: () { onPressed: _handleLogin,
if (_formKey.currentState?.validate() ?? false) {
// TODO: Implémenter la logique de connexion
}
},
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),

View File

@ -0,0 +1,21 @@
class ApiConfig {
// static const String baseUrl = 'https://ynov.ptits-pas.fr/api/v1';
static const String baseUrl = 'http://localhost:3000/api/v1';
// Auth endpoints
static const String login = '/auth/login';
static const String register = '/auth/register';
static const String refreshToken = '/auth/refresh';
// Users endpoints
static const String users = '/users';
static const String userProfile = '/users/profile';
static const String userChildren = '/users/children';
// Dashboard endpoints
static const String dashboard = '/dashboard';
static const String events = '/events';
static const String contracts = '/contracts';
static const String conversations = '/conversations';
static const String notifications = '/notifications';
}

View File

@ -1,9 +1,78 @@
import 'dart:convert'; import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:p_tits_pas/services/api/api_config.dart';
import '../models/user.dart'; import '../models/user.dart';
import 'package:http/http.dart' as http;
class AuthResponse {
final String acessToken;
final String role;
AuthResponse({required this.acessToken, required this.role});
factory AuthResponse.fromJson(Map<String, dynamic> json) {
return AuthResponse(
acessToken: json['acessToken'],
role: json['role'],
);
}
}
class AuthService { class AuthService {
static const String _usersKey = 'users'; ApiConfig apiConfig = ApiConfig();
String baseUrl = ApiConfig.baseUrl;
final storage = const FlutterSecureStorage();
//login
Future<AuthResponse> login(String email, String password) async {
final response = await http.post(
Uri.parse('$baseUrl${ApiConfig.login}'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'email': email, 'password': password}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final authResponse = AuthResponse.fromJson(data);
await storage.write(key: 'access_token', value: authResponse.acessToken);
await storage.write(key: 'role', value: authResponse.role);
return authResponse;
} else {
throw Exception('Failed to login');
}
}
//register
Future<AppUser> register({
required String email,
required String password,
required String firstName,
required String lastName,
required String role,
}) async {
final response = await http.post(
Uri.parse('$baseUrl${ApiConfig.register}'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': email,
'password': password,
'firstName': firstName,
'lastName': lastName,
'role': role,
}),
);
if (response.statusCode == 201) {
final data = jsonDecode(response.body);
return AppUser.fromJson(data['user']);
} else {
throw Exception('Failed to register');
}
}
/*static const String _usersKey = 'users';
static const String _parentsKey = 'parents'; static const String _parentsKey = 'parents';
static const String _childrenKey = 'children'; static const String _childrenKey = 'children';
@ -38,5 +107,5 @@ class AuthService {
// Méthode pour récupérer l'utilisateur connecté (mode démonstration) // Méthode pour récupérer l'utilisateur connecté (mode démonstration)
static Future<AppUser?> getCurrentUser() async { static Future<AppUser?> getCurrentUser() async {
return null; // Aucun utilisateur en mode démonstration return null; // Aucun utilisateur en mode démonstration
} }*/
} }

View File

@ -0,0 +1,20 @@
import 'package:flutter/cupertino.dart';
class NavigationService {
static void handleLoginSuccess(BuildContext context, String role) {
switch (role) {
case 'admin':
Navigator.pushReplacementNamed(context, '/admin_dashboard');
break;
case 'gestionnaire':
Navigator.pushReplacementNamed(context, '/gestionnaire_dashboard');
break;
case 'parent':
Navigator.pushReplacementNamed(context, '/parent-dashboard');
break;
case 'assistante_maternelle':
Navigator.pushReplacementNamed(context, '/assistante_maternelle_dashboard');
break;
}
}
}

View File

@ -139,6 +139,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.28" version: "2.0.28"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
url: "https://pub.dev"
source: hosted
version: "9.2.4"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
url: "https://pub.dev"
source: hosted
version: "1.2.3"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -169,10 +217,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.5.0"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -626,6 +674,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
url: "https://pub.dev"
source: hosted
version: "5.13.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -18,7 +18,8 @@ dependencies:
image_picker: ^1.0.7 image_picker: ^1.0.7
js: ^0.6.7 js: ^0.6.7
url_launcher: ^6.2.4 url_launcher: ^6.2.4
http: ^1.2.0 http: ^1.5.0
flutter_secure_storage: ^9.2.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -7,11 +7,14 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows file_selector_windows
flutter_secure_storage_windows
url_launcher_windows url_launcher_windows
) )