diff --git a/frontend/app_student/assets/images/3il-icon-blue.png b/frontend/app_student/assets/images/3il-icon-blue.png new file mode 100644 index 0000000..cb7e5b7 Binary files /dev/null and b/frontend/app_student/assets/images/3il-icon-blue.png differ diff --git a/frontend/app_student/assets/images/3il-icon-white.png b/frontend/app_student/assets/images/3il-icon-white.png new file mode 100644 index 0000000..0983585 Binary files /dev/null and b/frontend/app_student/assets/images/3il-icon-white.png differ diff --git a/frontend/app_student/assets/images/3il-icon.png b/frontend/app_student/assets/images/3il-icon.png new file mode 100644 index 0000000..5e45b2e Binary files /dev/null and b/frontend/app_student/assets/images/3il-icon.png differ diff --git a/frontend/app_student/assets/images/3il-logo-white.jpeg b/frontend/app_student/assets/images/3il-logo-white.jpeg new file mode 100644 index 0000000..2a2c509 Binary files /dev/null and b/frontend/app_student/assets/images/3il-logo-white.jpeg differ diff --git a/frontend/app_student/flutter_native_splash.yaml b/frontend/app_student/flutter_native_splash.yaml new file mode 100644 index 0000000..98a9f47 --- /dev/null +++ b/frontend/app_student/flutter_native_splash.yaml @@ -0,0 +1,143 @@ +flutter_native_splash: + # This package generates native code to customize Flutter's default white native splash screen + # with background color and splash image. + # Customize the parameters below, and run the following command in the terminal: + # dart run flutter_native_splash:create + # To restore Flutter's default white splash screen, run the following command in the terminal: + # dart run flutter_native_splash:remove + + # IMPORTANT NOTE: These parameter do not affect the configuration of Android 12 and later, which + # handle splash screens differently that prior versions of Android. Android 12 and later must be + # configured specifically in the android_12 section below. + + # color or background_image is the only required parameter. Use color to set the background + # of your splash screen to a solid color. Use background_image to set the background of your + # splash screen to a png image. This is useful for gradients. The image will be stretch to the + # size of the app. Only one parameter can be used, color and background_image cannot both be set. + color: "#42a5f5" + #background_image: "assets/background.png" + + # Optional parameters are listed below. To enable a parameter, uncomment the line by removing + # the leading # character. + + # The image parameter allows you to specify an image used in the splash screen. It must be a + # png file and should be sized for 4x pixel density. + #image: assets/images/3il-logo-white.jpeg + + # The branding property allows you to specify an image used as branding in the splash screen. + # It must be a png file. It is supported for Android, iOS and the Web. For Android 12, + # see the Android 12 section below. + branding: assets/images/3il-logo-white.jpeg + # To position the branding image at the bottom of the screen you can use bottom, bottomRight, + # and bottomLeft. The default values is bottom if not specified or specified something else. + #branding_mode: bottom + + # The color_dark, background_image_dark, image_dark, branding_dark are parameters that set the background + # and image when the device is in dark mode. If they are not specified, the app will use the + # parameters from above. If the image_dark parameter is specified, color_dark or + # background_image_dark must be specified. color_dark and background_image_dark cannot both be + # set. + #color_dark: "#042a49" + #background_image_dark: "assets/dark-background.png" + #image_dark: assets/splash-invert.png + #branding_dark: assets/dart_dark.png + + # From Android 12 onwards, the splash screen is handled differently than in previous versions. + # Please visit https://developer.android.com/guide/topics/ui/splash-screen + # Following are specific parameters for Android 12+. + android_12: + # The image parameter sets the splash screen icon image. If this parameter is not specified, + # the app's launcher icon will be used instead. + # Please note that the splash screen will be clipped to a circle on the center of the screen. + # App icon with an icon background: This should be 960×960 pixels, and fit within a circle + # 640 pixels in diameter. + # App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle + # 768 pixels in diameter. + #image: assets/android12splash.png + + # Splash screen background color. + #color: "#42a5f5" + + # App icon background color. + #icon_background_color: "#111111" + + # The branding property allows you to specify an image used as branding in the splash screen. + branding: assets/images/3il-logo-white.jpeg + + # The image_dark, color_dark, icon_background_color_dark, and branding_dark set values that + # apply when the device is in dark mode. If they are not specified, the app will use the + # parameters from above. + #image_dark: assets/android12splash-invert.png + #color_dark: "#042a49" + #icon_background_color_dark: "#eeeeee" + + # The android, ios and web parameters can be used to disable generating a splash screen on a given + # platform. + #android: false + #ios: false + #web: false + + # Platform specific images can be specified with the following parameters, which will override + # the respective parameter. You may specify all, selected, or none of these parameters: + #color_android: "#42a5f5" + #color_dark_android: "#042a49" + #color_ios: "#42a5f5" + #color_dark_ios: "#042a49" + #color_web: "#42a5f5" + #color_dark_web: "#042a49" + #image_android: assets/splash-android.png + #image_dark_android: assets/splash-invert-android.png + #image_ios: assets/splash-ios.png + #image_dark_ios: assets/splash-invert-ios.png + #image_web: assets/splash-web.gif + #image_dark_web: assets/splash-invert-web.gif + #background_image_android: "assets/background-android.png" + #background_image_dark_android: "assets/dark-background-android.png" + #background_image_ios: "assets/background-ios.png" + #background_image_dark_ios: "assets/dark-background-ios.png" + #background_image_web: "assets/background-web.png" + #background_image_dark_web: "assets/dark-background-web.png" + #branding_android: assets/brand-android.png + #branding_dark_android: assets/dart_dark-android.png + #branding_ios: assets/brand-ios.png + #branding_dark_ios: assets/dart_dark-ios.png + #branding_web: assets/brand-web.gif + #branding_dark_web: assets/dart_dark-web.gif + + # The position of the splash image can be set with android_gravity, ios_content_mode, and + # web_image_mode parameters. All default to center. + # + # android_gravity can be one of the following Android Gravity (see + # https://developer.android.com/reference/android/view/Gravity): bottom, center, + # center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, + # fill_vertical, left, right, start, or top. + #android_gravity: center + # + # ios_content_mode can be one of the following iOS UIView.ContentMode (see + # https://developer.apple.com/documentation/uikit/uiview/contentmode): scaleToFill, + # scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, + # bottomLeft, or bottomRight. + #ios_content_mode: center + # + # web_image_mode can be one of the following modes: center, contain, stretch, and cover. + #web_image_mode: center + + # The screen orientation can be set in Android with the android_screen_orientation parameter. + # Valid parameters can be found here: + # https://developer.android.com/guide/topics/manifest/activity-element#screen + #android_screen_orientation: sensorLandscape + + # To hide the notification bar, use the fullscreen parameter. Has no effect in web since web + # has no notification bar. Defaults to false. + # NOTE: Unlike Android, iOS will not automatically show the notification bar when the app loads. + # To show the notification bar, add the following code to your Flutter app: + # WidgetsFlutterBinding.ensureInitialized(); + # SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top], ); + #fullscreen: true + + # If you have changed the name(s) of your info.plist file(s), you can specify the filename(s) + # with the info_plist_files parameter. Remove only the # characters in the three lines below, + # do not remove any spaces: + #info_plist_files: + # - 'ios/Runner/Info-Debug.plist' + # - 'ios/Runner/Info-Release.plist' \ No newline at end of file diff --git a/frontend/app_student/lib/api/users/entities/user_entity.dart b/frontend/app_student/lib/api/users/entities/user_entity.dart index 5acce5b..7b84ead 100644 --- a/frontend/app_student/lib/api/users/entities/user_entity.dart +++ b/frontend/app_student/lib/api/users/entities/user_entity.dart @@ -1,14 +1,12 @@ -import 'package:app_student/api/class_groups/entities/class_group_entity.dart'; - class UserEntity { final String ine; final String firstName; final DateTime birthDate; - final ClassGroupEntity? classGroup; + final String? className; UserEntity( {required this.ine, required this.firstName, required this.birthDate, - this.classGroup}); + this.className = ''}); } diff --git a/frontend/app_student/lib/api/users/models/user_model.dart b/frontend/app_student/lib/api/users/models/user_model.dart index e8a8836..1e0cd12 100644 --- a/frontend/app_student/lib/api/users/models/user_model.dart +++ b/frontend/app_student/lib/api/users/models/user_model.dart @@ -8,4 +8,5 @@ class UserModel { String get file => entity.ine; String get name => entity.firstName; DateTime get birthDate => entity.birthDate; + String? get className => entity.className ?? ''; } diff --git a/frontend/app_student/lib/api/users/repositories/user_repository.dart b/frontend/app_student/lib/api/users/repositories/user_repository.dart new file mode 100644 index 0000000..6f3d76a --- /dev/null +++ b/frontend/app_student/lib/api/users/repositories/user_repository.dart @@ -0,0 +1,57 @@ +import 'package:app_student/api/users/models/user_model.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../entities/user_entity.dart'; + +class UserRepository { + Future getUser() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + String? ine = prefs.getString('ine'); + String? name = prefs.getString('name'); + String? birthDate = prefs.getString('birthDate'); + String? className = prefs.getString('className'); + if (ine != null && name != null && birthDate != null) { + return UserModel( + entity: UserEntity( + ine: ine, + firstName: name, + birthDate: DateTime.parse(birthDate), + className: className, + ), + ); + } else { + throw Exception('Utilisateur non trouvé'); + } + } + + Future saveUserDetails( + String ine, String name, String birthDate, String className) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + UserModel( + entity: UserEntity( + ine: ine, + firstName: name, + birthDate: DateTime.parse(birthDate), + className: className, + ), + ); + await prefs.setString('ine', ine); + await prefs.setString('name', name); + await prefs.setString('birthDate', birthDate); + await prefs.setString('className', className); + } + + Future deleteUser() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.remove('ine'); + await prefs.remove('name'); + await prefs.remove('birthDate'); + await prefs.remove('className'); + } + + Future saveUserClass(String className) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('className', className); + } +} diff --git a/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repository.dart b/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repository.dart index 8eaf13e..0f303cc 100644 --- a/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repository.dart +++ b/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repository.dart @@ -5,7 +5,7 @@ import 'package:app_student/api/week_schedule/models/week_schedule_model.dart'; class WeekScheduleRepository { final ApiService apiService; - WeekScheduleRepository({required this.apiService, required String className}); + WeekScheduleRepository({required this.apiService}); Future> getWeeksSchedule(className) { return apiService.getData('/api/timetable?class_param=$className', (item) { diff --git a/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart b/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart index cd2c584..1622392 100644 --- a/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart +++ b/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart @@ -1,22 +1,37 @@ import 'package:app_student/api/class_groups/models/class_group_model.dart'; -import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:app_student/api/users/models/user_model.dart'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; +import '../../api/class_groups/repositories/class_group_repository.dart'; +import '../../api/users/repositories/user_repository.dart'; + part 'class_group_state.dart'; class ClassGroupCubit extends Cubit { final ClassGroupRepository classRepository; + final UserRepository userRepository; - ClassGroupCubit({required this.classRepository}) : super(ClassGroupInitial()); + ClassGroupCubit({required this.classRepository, required this.userRepository}) + : super(ClassGroupInitial()); Future fetchClasses() async { try { emit(ClassGroupLoading()); final classes = await classRepository.getClasses(); + emit(ClassGroupLoaded(classes)); } catch (e) { emit(ClassGroupError(e.toString())); } } + + Future getConnectedUser() async { + return userRepository.getUser(); + } + + Future saveClass(ClassGroupModel classGroup) async { + await userRepository.saveUserClass(classGroup.name.toString()); + emit(ClassGroupSelected()); + } } diff --git a/frontend/app_student/lib/class_groups/cubit/class_group_state.dart b/frontend/app_student/lib/class_groups/cubit/class_group_state.dart index 7ee2c23..b1ac586 100644 --- a/frontend/app_student/lib/class_groups/cubit/class_group_state.dart +++ b/frontend/app_student/lib/class_groups/cubit/class_group_state.dart @@ -16,5 +16,7 @@ class ClassGroupLoaded extends ClassGroupState { class ClassGroupError extends ClassGroupState { final String message; - ClassGroupError(this.message); + ClassGroupError([this.message = 'An error occurred']); } + +class ClassGroupSelected extends ClassGroupState {} diff --git a/frontend/app_student/lib/class_groups/views/class_group_page.dart b/frontend/app_student/lib/class_groups/views/class_group_page.dart new file mode 100644 index 0000000..174b001 --- /dev/null +++ b/frontend/app_student/lib/class_groups/views/class_group_page.dart @@ -0,0 +1,70 @@ +import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:app_student/class_groups/cubit/class_group_cubit.dart'; +import 'package:app_student/class_groups/views/widgets/card_list.dart'; +import 'package:app_student/class_groups/views/widgets/header/header_logo.dart'; +import 'package:app_student/class_groups/views/widgets/header/header_text.dart'; +import 'package:app_student/class_groups/views/widgets/header/header_title.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +import '../../api/users/models/user_model.dart'; +import '../../api/users/repositories/user_repository.dart'; + +class ClassGroupPage extends StatelessWidget { + const ClassGroupPage({super.key}); + + @override + Widget build(BuildContext context) { + final classRepository = + RepositoryProvider.of(context); + final userRepository = RepositoryProvider.of(context); + final classCubit = ClassGroupCubit( + classRepository: classRepository, userRepository: userRepository); + + return BlocProvider( + create: (context) => classCubit..fetchClasses(), + child: FutureBuilder( + future: context.read().getConnectedUser(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erreur: ${snapshot.error}')); + } else { + final user = snapshot.data; + return Scaffold( + body: BlocBuilder( + builder: (context, state) { + if (state is ClassGroupLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is ClassGroupSelected) { + WidgetsBinding.instance.addPostFrameCallback((_) { + context.go('/schedule'); + }); + return const SizedBox.shrink(); + } else if (state is ClassGroupLoaded) { + return Column( + children: [ + const HeaderLogo(), + HeaderTitle('Bonjour, ${user?.name}'), + const HeaderText('Choisis ta promotion :'), + Expanded( + child: CardList(classesList: state.classes), + ), + ], + ); + } else if (state is ClassGroupError) { + return Center(child: Text(state.message)); + } else { + return const SizedBox.shrink(); + } + }, + ), + ); + } + }, + ), + ); + } +} diff --git a/frontend/app_student/lib/class_groups/views/class_group_view.dart b/frontend/app_student/lib/class_groups/views/class_group_view.dart deleted file mode 100644 index 27faea6..0000000 --- a/frontend/app_student/lib/class_groups/views/class_group_view.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; -import 'package:app_student/class_groups/cubit/class_group_cubit.dart'; -import 'package:app_student/class_groups/views/widgets/card_list.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:app_student/class_groups/views/widgets/header/header_logo.dart'; -import 'package:app_student/class_groups/views/widgets/header/header_text.dart'; -import 'package:app_student/class_groups/views/widgets/header/header_title.dart'; - -class ClassListPage extends StatelessWidget { - const ClassListPage({super.key}); - - @override - Widget build(BuildContext context) { - final classRepository = - RepositoryProvider.of(context); - final classCubit = ClassGroupCubit(classRepository: classRepository); - - return BlocProvider( - create: (context) => classCubit..fetchClasses(), - child: Scaffold( - body: BlocBuilder( - builder: (context, state) { - if (state is ClassGroupLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is ClassGroupLoaded) { - return Column( - children: [ - const HeaderLogo(), - const HeaderTitle('Bonjour, ####'), - const HeaderText('Dans quelle classe êtes-vous ?'), - Expanded( - child: CardList(classesList: state.classes), - ), - ], - ); - } else if (state is ClassGroupError) { - return Center(child: Text(state.message)); - } else { - return const SizedBox.shrink(); - } - }, - ), - ), - ); - } -} diff --git a/frontend/app_student/lib/class_groups/views/widgets/card_list.dart b/frontend/app_student/lib/class_groups/views/widgets/card_list.dart index a866abd..c20ecb0 100644 --- a/frontend/app_student/lib/class_groups/views/widgets/card_list.dart +++ b/frontend/app_student/lib/class_groups/views/widgets/card_list.dart @@ -1,5 +1,8 @@ import 'package:app_student/api/class_groups/models/class_group_model.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../cubit/class_group_cubit.dart'; class CardList extends StatelessWidget { final List classesList; @@ -12,11 +15,14 @@ class CardList extends StatelessWidget { itemCount: classesList.length, itemBuilder: (context, index) { return Card( - margin: const EdgeInsets.all(8.0), + margin: const EdgeInsets.only(left: 25.0, right: 25.0, top: 10.0), child: ListTile( title: Text( classesList[index].name, ), + onTap: () { + context.read().saveClass(classesList[index]); + }, ), ); }, diff --git a/frontend/app_student/lib/class_groups/views/widgets/header/header_logo.dart b/frontend/app_student/lib/class_groups/views/widgets/header/header_logo.dart index 94cb00e..3aa3ab3 100644 --- a/frontend/app_student/lib/class_groups/views/widgets/header/header_logo.dart +++ b/frontend/app_student/lib/class_groups/views/widgets/header/header_logo.dart @@ -5,15 +5,17 @@ class HeaderLogo extends StatelessWidget { @override Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Image.asset( - 'assets/images/3il-logo.jpg', + return AppBar( + backgroundColor: const Color(0xFF005067), + toolbarHeight: 70.0, + title: Opacity( + opacity: 0.5, + child: Image.asset( + 'assets/images/3il-icon-white.png', fit: BoxFit.contain, - height: 32, ), - ], + ), + centerTitle: true, ); } } diff --git a/frontend/app_student/lib/class_groups/views/widgets/header/header_text.dart b/frontend/app_student/lib/class_groups/views/widgets/header/header_text.dart index c8b71a4..ff6d4b2 100644 --- a/frontend/app_student/lib/class_groups/views/widgets/header/header_text.dart +++ b/frontend/app_student/lib/class_groups/views/widgets/header/header_text.dart @@ -8,10 +8,7 @@ class HeaderText extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.only( - top: 5.0, - left: 25.0, - bottom: 10), // Ajout d'un espacement à gauche de 10px + padding: const EdgeInsets.only(top: 5.0, left: 25.0), child: Align( alignment: Alignment.centerLeft, child: Text( diff --git a/frontend/app_student/lib/class_groups/views/widgets/header/header_title.dart b/frontend/app_student/lib/class_groups/views/widgets/header/header_title.dart index 516a4e7..35f3e0a 100644 --- a/frontend/app_student/lib/class_groups/views/widgets/header/header_title.dart +++ b/frontend/app_student/lib/class_groups/views/widgets/header/header_title.dart @@ -8,10 +8,7 @@ class HeaderTitle extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.only( - top: 20.0, - left: 25.0, - bottom: 10), // Ajout un espacement à gauche de 10px + padding: const EdgeInsets.only(top: 20.0, left: 25.0, bottom: 2), child: Align( alignment: Alignment.centerLeft, child: Text( diff --git a/frontend/app_student/lib/login/cubit/login_cubit.dart b/frontend/app_student/lib/login/cubit/login_cubit.dart index 631c84a..ba67eff 100644 --- a/frontend/app_student/lib/login/cubit/login_cubit.dart +++ b/frontend/app_student/lib/login/cubit/login_cubit.dart @@ -1,11 +1,13 @@ +import 'package:app_student/api/users/repositories/user_repository.dart'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'login_state.dart'; class LoginCubit extends Cubit { - LoginCubit() : super(LoginInitial()) { + final UserRepository userRepository; + + LoginCubit(this.userRepository) : super(LoginInitial()) { checkUserAuthentication(); } @@ -23,24 +25,18 @@ class LoginCubit extends Cubit { emit(LoginFieldError()); return; } - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setString('ine', ine); - await prefs.setString('name', name); - await prefs.setString('birthDate', birthDate); + await userRepository.saveUserDetails(ine, name, birthDate, ''); emit(RedirectToClassSelection()); } - Future checkUserAuthentication() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String? ine = prefs.getString('ine'); - String? name = prefs.getString('name'); - String? birthDate = prefs.getString('birthDate'); - String? className = prefs.getString('className'); - - if (ine != null && name != null && birthDate != null && className != null) { + Future checkUserAuthentication() async { + try { + await userRepository.getUser(); emit(LoginAuthenticated()); - } else { + return true; + } catch (e) { emit(LoginInitial()); + return false; } } } diff --git a/frontend/app_student/lib/login/views/login_page.dart b/frontend/app_student/lib/login/views/login_page.dart index 7c4c658..9b7a0e9 100644 --- a/frontend/app_student/lib/login/views/login_page.dart +++ b/frontend/app_student/lib/login/views/login_page.dart @@ -1,16 +1,10 @@ -import 'package:app_student/api/api_service.dart'; -import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; -import 'package:app_student/config/config.dart'; import 'package:app_student/login/cubit/login_cubit.dart'; import 'package:app_student/login/widgets/form/form_login.dart'; import 'package:app_student/login/widgets/header/header_text.dart'; -import 'package:app_student/week_schedule/views/week_schedule.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:provider/provider.dart'; +import 'package:go_router/go_router.dart'; -import '../../class_groups/cubit/class_group_cubit.dart'; -import '../../class_groups/views/class_group_view.dart'; import '../widgets/header/header_logo.dart'; class LoginPage extends StatelessWidget { @@ -22,16 +16,7 @@ class LoginPage extends StatelessWidget { builder: (context, state) { if (state is RedirectToClassSelection) { WidgetsBinding.instance.addPostFrameCallback((_) { - final config = Provider.of(context, listen: false); - Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => BlocProvider( - create: (_) => ClassGroupCubit( - classRepository: ClassGroupRepository( - apiService: ApiService(apiUrl: config.apiUrl))), - child: const ClassListPage()), - ), - ); + context.go('/classList'); }); return Container(); } else if (state is LoginInitial) { @@ -46,9 +31,7 @@ class LoginPage extends StatelessWidget { ); } else if (state is LoginAuthenticated) { WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (context) => const WeekSchedulePage()), - ); + context.go('/schedule'); }); return Container(); } else if (state is LoginFieldError) { diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart index f1e1723..aa4b195 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -1,29 +1,15 @@ import 'package:app_student/config/dev_config.dart'; +import 'package:app_student/routes.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; -import 'api/api_service.dart'; -import 'api/class_groups/repositories/class_group_repository.dart'; import 'config/config.dart'; -import 'login/cubit/login_cubit.dart'; -import 'login/views/login_page.dart'; void main() { - final ClassGroupRepository classGroupRepository = ClassGroupRepository( - apiService: ApiService(apiUrl: DevConfig().apiUrl), - ); - runApp( - MultiProvider( - providers: [ - Provider( - create: (_) => DevConfig(), - ), - RepositoryProvider( - create: (context) => classGroupRepository, - ), - ], + Provider( + create: (_) => DevConfig(), child: const MyApp(), ), ); @@ -34,17 +20,28 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Class List', + final router = GoRouter( + routes: AppRoutes.routes, + initialLocation: '/login', + errorPageBuilder: (context, state) { + return MaterialPage( + child: Scaffold( + body: Center( + child: Text('Page not found: ${state.uri}'), + ), + ), + ); + }, + ); + + return MaterialApp.router( + title: '3iL Student App', theme: ThemeData( primarySwatch: Colors.blue, focusColor: const Color(0xffE84E0F), fontFamily: 'Arial', ), - home: BlocProvider( - create: (context) => LoginCubit(), - child: const LoginPage(), - ), + routerConfig: router, ); } } diff --git a/frontend/app_student/lib/main_test.dart b/frontend/app_student/lib/main_test.dart deleted file mode 100644 index c11a57d..0000000 --- a/frontend/app_student/lib/main_test.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:app_student/api/week_schedule/repositories/week_schedule_repository.dart'; -import 'package:app_student/config/dev_config.dart'; -import 'package:app_student/week_schedule/views/week_schedule.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:provider/provider.dart'; - -import 'api/api_service.dart'; -import 'api/class_groups/repositories/class_group_repository.dart'; -import 'config/config.dart'; - -void main() { - final ClassGroupRepository classGroupRepository = ClassGroupRepository( - apiService: ApiService(apiUrl: DevConfig().apiUrl), - ); - - runApp( - MultiProvider( - providers: [ - Provider( - create: (_) => DevConfig(), - ), - RepositoryProvider( - create: (context) => classGroupRepository, - ), - ], - child: const MyApp(), - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return Provider( - create: (_) => DevConfig(), - child: RepositoryProvider( - create: (context) => WeekScheduleRepository( - apiService: ApiService(apiUrl: context.read().apiUrl), - className: 'B3 Groupe 3 DLW-FA'), - child: const MaterialApp(home: WeekSchedulePage()), - ), - ); - } -} diff --git a/frontend/app_student/lib/routes.dart b/frontend/app_student/lib/routes.dart new file mode 100644 index 0000000..f0417d9 --- /dev/null +++ b/frontend/app_student/lib/routes.dart @@ -0,0 +1,75 @@ +// lib/routes.dart +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:app_student/api/users/repositories/user_repository.dart'; +import 'package:app_student/api/week_schedule/repositories/week_schedule_repository.dart'; +import 'package:app_student/class_groups/cubit/class_group_cubit.dart'; +import 'package:app_student/config/config.dart'; +import 'package:app_student/week_schedule/cubit/week_schedule_cubit.dart'; +import 'package:app_student/week_schedule/views/week_schedule.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +import 'class_groups/views/class_group_page.dart'; +import 'login/cubit/login_cubit.dart'; +import 'login/views/login_page.dart'; + +class AppRoutes { + static const classListPage = '/classList'; + static const loginPage = '/login'; + static const schedulePage = '/schedule'; + + static final routes = [ + GoRoute( + path: classListPage, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: MultiRepositoryProvider( + providers: [ + RepositoryProvider( + create: (context) => ClassGroupRepository( + apiService: ApiService( + apiUrl: context.read().apiUrl))), + RepositoryProvider(create: (context) => UserRepository()), + ], + child: BlocProvider( + create: (context) => ClassGroupCubit( + classRepository: context.read(), + userRepository: context.read()), + child: const ClassGroupPage())))), + GoRoute( + path: loginPage, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: RepositoryProvider( + create: (context) => UserRepository(), + child: BlocProvider( + create: (context) => LoginCubit(context.read()), + child: const LoginPage(), + ), + ), + ), + ), + GoRoute( + path: schedulePage, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: MultiRepositoryProvider( + providers: [ + RepositoryProvider(create: (context) { + return WeekScheduleRepository( + apiService: + ApiService(apiUrl: context.read().apiUrl)); + }), + RepositoryProvider(create: (context) => UserRepository()), + ], + child: BlocProvider( + create: (context) => WeekScheduleCubit( + weekScheduleRepository: + context.read(), + userRepository: context.read(), + ), + child: const WeekSchedulePage())))), + ]; +} diff --git a/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart b/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart index fa507ff..65b7672 100644 --- a/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart +++ b/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart @@ -2,14 +2,22 @@ import 'package:app_student/api/week_schedule/repositories/week_schedule_reposit import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; +import '../../api/users/repositories/user_repository.dart'; + part 'week_schedule_state.dart'; class WeekScheduleCubit extends Cubit { final WeekScheduleRepository weekScheduleRepository; + final UserRepository userRepository; - WeekScheduleCubit({required this.weekScheduleRepository}) + WeekScheduleCubit( + {required this.weekScheduleRepository, required this.userRepository}) : super(WeekScheduleInitial()); + Future getUser() async { + return await userRepository.getUser(); + } + Future fetchWeekSchedule(String className) async { try { emit(WeekScheduleLoading()); @@ -20,4 +28,9 @@ class WeekScheduleCubit extends Cubit { emit(WeekScheduleError(e.toString())); } } + + void fetchUserAndSchedule() async { + final user = await getUser(); + fetchWeekSchedule(user.className); + } } diff --git a/frontend/app_student/lib/week_schedule/views/week_schedule.dart b/frontend/app_student/lib/week_schedule/views/week_schedule.dart index 616e516..57ca24d 100644 --- a/frontend/app_student/lib/week_schedule/views/week_schedule.dart +++ b/frontend/app_student/lib/week_schedule/views/week_schedule.dart @@ -5,6 +5,8 @@ import 'package:app_student/week_schedule/views/widgets/components/custom_appbar import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../api/users/repositories/user_repository.dart'; + class WeekSchedulePage extends StatelessWidget { const WeekSchedulePage({super.key}); @@ -12,12 +14,13 @@ class WeekSchedulePage extends StatelessWidget { Widget build(BuildContext context) { final weekScheduleRepository = RepositoryProvider.of(context); - final weekScheduleCubit = - WeekScheduleCubit(weekScheduleRepository: weekScheduleRepository); + final userRepository = RepositoryProvider.of(context); + final weekScheduleCubit = WeekScheduleCubit( + weekScheduleRepository: weekScheduleRepository, + userRepository: userRepository); return BlocProvider( - create: (context) => - weekScheduleCubit..fetchWeekSchedule('B3 Groupe 3 DLW-FA'), + create: (context) => weekScheduleCubit..fetchUserAndSchedule(), child: Scaffold( appBar: const AppBarWeekSchedule(), body: BlocBuilder( diff --git a/frontend/app_student/pubspec.lock b/frontend/app_student/pubspec.lock index 4d39df3..7bae58c 100644 --- a/frontend/app_student/pubspec.lock +++ b/frontend/app_student/pubspec.lock @@ -128,6 +128,14 @@ packages: description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "170c46e237d6eb0e6e9f0e8b3f56101e14fb64f787016e42edd74c39cf8b176a" + url: "https://pub.dev" + source: hosted + version: "13.2.0" http: dependency: "direct main" description: @@ -176,6 +184,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index f62920b..78eaa91 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: flutter_bloc: ^8.1.4 meta: ^1.11.0 flutter_svg: ^2.0.10+1 + go_router: ^13.2.0 dev_dependencies: flutter_test: @@ -95,6 +96,7 @@ flutter: # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # + fonts: - family: Arial fonts: