diff --git a/.gitignore b/.gitignore index cfc5ff8..6f8b4b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .idea/ .vscode/ -backend/vendor/ -/backend/.php-cs-fixer.cache \ No newline at end of file +backend/vendor/ \ No newline at end of file diff --git a/backend/.php-cs-fixer.cache b/backend/.php-cs-fixer.cache new file mode 100644 index 0000000..39b28d7 --- /dev/null +++ b/backend/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.2.12","version":"3.51.0:v3.51.0#127fa74f010da99053e3f5b62672615b72dd6efd","indent":" ","lineEnding":"\n","rules":{"align_multiline_comment":true,"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"class_reference_name_casing":true,"clean_namespace":true,"concat_space":true,"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_declaration":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":false,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["attribute","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_hidden_params":true,"remove_inheritdoc":true},"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"nullable_type_declaration":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"ordered_types":{"null_adjustment":"always_last","sort_algorithm":"none"},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":{"groups":[["Annotation","NamedArgumentConstructor","Target"],["author","copyright","license"],["category","package","subpackage"],["property","property-read","property-write"],["deprecated","link","see","since"]]},"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_around_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"statement_indentation":{"stick_comment_to_next_continuous_control_statement":true},"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"type_declaration_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"cast_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\\Controller\\Api\\ClassesController.php":"3edd9a69cd0992943c7f4e35e189a328","src\\Controller\\Api\\TimetableController.php":"c9b1d63edf22527d74b68c59b9bd06eb","src\\Entity\\ClassGroups.php":"6bc6445daf9e43c60078656be46a06d4","src\\Entity\\DaySchedule.php":"56b4524fd686bf525de63858705ecfb2","src\\Entity\\Event.php":"6cbed0ffedbaccef2e8935c1a584977b","src\\Entity\\EventHours.php":"b437c36942eca2170869a2a7aaf2fb70","src\\Entity\\WeekSchedule.php":"a622ed2509b48533d6d6bc79a42dbdcc","src\\Kernel.php":"065c02fc2d62bfff3cd16f3a0b46d9ee","src\\Repository\\ClassGroupsRepository.php":"92142c8ee810ca31f60bffab20a87b0d","src\\Repository\\DayScheduleRepository.php":"98703542528a91067ab9ce94728d37d2","src\\Repository\\EventHoursRepository.php":"ae0ae56be4254d2d2e0d0dda12c03723","src\\Repository\\EventRepository.php":"8be5088a65ffa6accaf4ccdbfb2f203b","src\\Repository\\WeekScheduleRepository.php":"49e3ea686a24c1193a983c024d08d22c","src\\Service\\ClassesScraperService.php":"d1961c5983c33c3fb970892a4976baa7","src\\Service\\JsonService.php":"130ad9ecda589dd98c6788ee552b66c1","src\\Service\\TimetableService.php":"efc4917fd4b881eac4e2c6d481b38904","src\\Service\\XMLService.php":"8db6a1d71c24b578809ad0793f2bd9d6","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder\\.php-cs-fixer.dist.php":"4a5cd9e046891dc468fde4a126674df1","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder1\\.php-cs-fixer.dist.php":"4a5cd9e046891dc468fde4a126674df1","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder8214\\src\\Service\\TimetableService.php":"efc4917fd4b881eac4e2c6d481b38904","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder5526\\src\\Service\\TimetableService.php":"efc4917fd4b881eac4e2c6d481b38904","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder563\\src\\Service\\TimetableService.php":"efc4917fd4b881eac4e2c6d481b38904","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder10365\\src\\Service\\TimetableService.php":"076a4cfe4213624431ce14be32f85675","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder9018\\src\\Service\\TimetableService.php":"13c4b244ab3280ab8f15336cded328eb","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder2666\\src\\Entity\\Event.php":"a31cdea7428a9d5caf1e22be96dc4409","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder4489\\src\\Service\\TimetableService.php":"13c4b244ab3280ab8f15336cded328eb","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder2522\\src\\Entity\\Event.php":"a31cdea7428a9d5caf1e22be96dc4409","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder8603\\src\\Service\\TimetableService.php":"13c4b244ab3280ab8f15336cded328eb","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder2585\\src\\Entity\\Event.php":"a31cdea7428a9d5caf1e22be96dc4409","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder7329\\src\\Service\\TimetableService.php":"13c4b244ab3280ab8f15336cded328eb","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder2168\\src\\Entity\\Event.php":"a31cdea7428a9d5caf1e22be96dc4409","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder752\\src\\Service\\TimetableService.php":"13c4b244ab3280ab8f15336cded328eb"}} \ No newline at end of file diff --git a/frontend/app_student/lib/api/api_service.dart b/frontend/app_student/lib/api/api_service.dart index a6cbf06..9d73118 100644 --- a/frontend/app_student/lib/api/api_service.dart +++ b/frontend/app_student/lib/api/api_service.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; @@ -12,7 +13,11 @@ class ApiService { String fullUrl = '$apiUrl$endpoint'; fullUrl = Uri.encodeFull(fullUrl); try { - final response = await http.get(Uri.parse(fullUrl)); + final response = await http + .get(Uri.parse(fullUrl)) + .timeout(const Duration(seconds: 20), onTimeout: () { + throw TimeoutException('The connection has timed out!'); + }); if (response.statusCode == 200) { List jsonResponse = json.decode(response.body); @@ -26,6 +31,8 @@ class ApiService { } else { throw Exception('ERROR ${response.statusCode} Failed to load data'); } + } on TimeoutException catch (e) { + throw Exception('Request time out: $e'); } catch (e) { throw Exception('Failed to load data: $e'); } diff --git a/frontend/app_student/lib/api/events/entities/event_entity.dart b/frontend/app_student/lib/api/events/entities/event_entity.dart index a5baeac..2390cb8 100644 --- a/frontend/app_student/lib/api/events/entities/event_entity.dart +++ b/frontend/app_student/lib/api/events/entities/event_entity.dart @@ -8,6 +8,7 @@ class EventEntity { final EventHoursEntity horaires; final String salle; final bool visio; + final bool repas; EventEntity({ required this.id, @@ -17,6 +18,7 @@ class EventEntity { required this.horaires, required this.salle, required this.visio, + required this.repas, }); factory EventEntity.fromJson(Map json) { @@ -28,6 +30,7 @@ class EventEntity { horaires: EventHoursEntity.fromJson(json['horaire']), salle: json['salle'] ?? 'null', visio: json['visio'] ?? false, + repas: json['repas'] ?? false, ); } } diff --git a/frontend/app_student/lib/api/events/models/event_model.dart b/frontend/app_student/lib/api/events/models/event_model.dart index 659d7e4..9bdaddc 100644 --- a/frontend/app_student/lib/api/events/models/event_model.dart +++ b/frontend/app_student/lib/api/events/models/event_model.dart @@ -13,4 +13,5 @@ class EventModel { EventHoursModel get horaires => EventHoursModel(entity: entity.horaires); String get salle => entity.salle; bool get visio => entity.visio; + bool get repas => entity.repas; } 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 1622392..46a4375 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,19 +1,15 @@ import 'package:app_student/api/class_groups/models/class_group_model.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, required this.userRepository}) - : super(ClassGroupInitial()); + ClassGroupCubit({required this.classRepository}) : super(ClassGroupInitial()); Future fetchClasses() async { try { @@ -25,13 +21,4 @@ class ClassGroupCubit extends Cubit { 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 b1ac586..acdc666 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 @@ -18,5 +18,3 @@ class ClassGroupError extends ClassGroupState { 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 index 9b4141d..8f98f17 100644 --- a/frontend/app_student/lib/class_groups/views/class_group_page.dart +++ b/frontend/app_student/lib/class_groups/views/class_group_page.dart @@ -4,13 +4,12 @@ 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:app_student/users/cubit/user_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.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}); @@ -19,52 +18,52 @@ class ClassGroupPage extends StatelessWidget { Widget build(BuildContext context) { final classRepository = RepositoryProvider.of(context); - final userRepository = RepositoryProvider.of(context); final classCubit = ClassGroupCubit( - classRepository: classRepository, userRepository: userRepository); + classRepository: classRepository, + )..fetchClasses(); return BlocProvider( - create: (context) => classCubit..fetchClasses(), + create: (context) => classCubit, child: FutureBuilder( - future: context.read().getConnectedUser(), + future: context.read().getCurrentUser(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { - return Center( - child: Text(AppLocalizations.of(context)! - .genericError(snapshot.error.toString()))); + 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) { + body: BlocListener( + listener: (context, state) { + if (state is UserClassesSelected) { WidgetsBinding.instance.addPostFrameCallback((_) { context.go('/schedule'); }); - return const SizedBox.shrink(); - } else if (state is ClassGroupLoaded) { - return Column( - children: [ - const HeaderLogo(), - HeaderTitle(AppLocalizations.of(context)! - .classSelectionTitle(user!.name)), - HeaderText(AppLocalizations.of(context)! - .classSelectionSubtitle), - Expanded( - child: CardList(classesList: state.classes), - ), - ], - ); - } else if (state is ClassGroupError) { - return Center(child: Text(state.message)); - } else { - return const SizedBox.shrink(); } }, + child: BlocBuilder( + builder: (context, state) { + if (state is ClassGroupLoading) { + return const Center(child: CircularProgressIndicator()); + } 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/widgets/card_list.dart b/frontend/app_student/lib/class_groups/views/widgets/card_list.dart index c20ecb0..0d7dca5 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,9 +1,8 @@ import 'package:app_student/api/class_groups/models/class_group_model.dart'; +import 'package:app_student/users/cubit/user_cubit.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; @@ -21,7 +20,7 @@ class CardList extends StatelessWidget { classesList[index].name, ), onTap: () { - context.read().saveClass(classesList[index]); + context.read().saveUserClass(classesList[index]); }, ), ); diff --git a/frontend/app_student/lib/l10n/app_fr.arb b/frontend/app_student/lib/l10n/app_fr.arb index 57e0c37..0b9c6f7 100644 --- a/frontend/app_student/lib/l10n/app_fr.arb +++ b/frontend/app_student/lib/l10n/app_fr.arb @@ -10,27 +10,27 @@ } }, "genericError": "Une erreur est survenue", - "@genericError": { - "description": "Message d'erreur générique", - "placeholders": { - "error": { - "type": "String", - "example": "Une erreur est survenue" - } - } - }, + "@genericError": { + "description": "Message d'erreur générique", + "placeholders": { + "error": { + "type": "String", + "example": "Une erreur est survenue" + } + } + }, "loginButton": "Connexion", - "@loginButton": { - "description": "Texte du bouton de connexion" - }, + "@loginButton": { + "description": "Texte du bouton de connexion" + }, "loginFirstNameLabel": "Prénom", - "@loginFirstName": { - "description": "Texte du label pour le prénom" - }, + "@loginFirstName": { + "description": "Texte du label pour le prénom" + }, "loginFirstNameHint": "Entrez votre prénom", - "@loginFirstNameHint": { - "description": "Texte de l'attribut placeholder pour le prénom" - }, + "@loginFirstNameHint": { + "description": "Texte de l'attribut placeholder pour le prénom" + }, "loginIneLabel": "INE", "@loginIneLabel": { "description": "Texte du label pour le numéro INE" @@ -44,29 +44,39 @@ "description": "Texte du label pour la date de naissance" }, "loginWelcomeTitle": "Bonjour :)", - "@loginWelcomeTitle": { - "description": "Texte d'accueil de la page de connexion" - }, + "@loginWelcomeTitle": { + "description": "Texte d'accueil de la page de connexion" + }, "loginWelcomeTitleError": "Oops, ca va aller", - "@loginWelcomeTitleError": { - "description": "Texte d'erreur d'accueil de la page de connexion" - }, + "@loginWelcomeTitleError": { + "description": "Texte d'erreur d'accueil de la page de connexion" + }, "loginFieldError": "Veuillez remplir tous les champs", - "@loginFieldError": { - "description": "Message d'erreur pour les champs de connexion" - }, + "@loginFieldError": { + "description": "Message d'erreur pour les champs de connexion" + }, "classSelectionTitle": "Salut {firstName} !", - "@classSelectionTitle": { - "description": "Texte d'accueil de la page de sélection de classe", - "placeholders": { - "firstName": { - "type": "String", - "example": "John" - } - } - }, - "classSelectionSubtitle": "Sélectionne ta classe", - "@classSelectionSubtitle": { - "description": "Texte de sous-titre de la page de sélection de classe" + "@classSelectionTitle": { + "description": "Texte d'accueil de la page de sélection de classe", + "placeholders": { + "firstName": { + "type": "String", + "example": "John" + } } + }, + "classSelectionSubtitle": "Sélectionne ta classe", + "@classSelectionSubtitle": { + "description": "Texte de sous-titre de la page de sélection de classe" + }, + "eventDuration": "1h30", + "@eventDuration": { + "description": "La durée de l'événement", + "example": "1h30" + }, + "roomLabel": "Salle", + "@roomLabel": { + "description": "Label pour la salle", + "example": "Salle" + } } \ No newline at end of file diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart index c8b8bce..9ba04b1 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -2,21 +2,25 @@ import 'package:app_student/config/dev_config.dart'; import 'package:app_student/routes.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:go_router/go_router.dart'; +import 'package:intl/date_symbol_data_local.dart'; import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'config/config.dart'; void main() async { await dotenv.load(); - runApp( - Provider( - create: (_) => DevConfig(), - child: const MyApp(), - ), - ); + + initializeDateFormatting('fr_FR', null).then((_) { + runApp( + Provider( + create: (_) => DevConfig(), + child: const MyApp(), + ), + ); + }); } class MyApp extends StatelessWidget { @@ -40,6 +44,7 @@ class MyApp extends StatelessWidget { ); return MaterialApp.router( + debugShowCheckedModeBanner: false, title: '3iL Student App', theme: ThemeData( primarySwatch: Colors.blue, diff --git a/frontend/app_student/lib/routes.dart b/frontend/app_student/lib/routes.dart index f0417d9..4f0fe7d 100644 --- a/frontend/app_student/lib/routes.dart +++ b/frontend/app_student/lib/routes.dart @@ -5,6 +5,7 @@ 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/users/cubit/user_cubit.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'; @@ -22,22 +23,32 @@ class AppRoutes { 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())))), + 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: MultiBlocProvider(providers: [ + BlocProvider( + create: (context) => ClassGroupCubit( + classRepository: context.read(), + )..fetchClasses(), + ), + BlocProvider( + create: (context) => + UserCubit(userRepository: context.read()) + ..fetchUser(), + ), + ], child: const ClassGroupPage()), + ), + ), + ), GoRoute( path: loginPage, pageBuilder: (context, state) => MaterialPage( @@ -56,20 +67,31 @@ class AppRoutes { pageBuilder: (context, state) => MaterialPage( key: state.pageKey, child: MultiRepositoryProvider( - providers: [ - RepositoryProvider(create: (context) { - return WeekScheduleRepository( + providers: [ + RepositoryProvider( + create: (context) => WeekScheduleRepository( apiService: - ApiService(apiUrl: context.read().apiUrl)); - }), - RepositoryProvider(create: (context) => UserRepository()), - ], - child: BlocProvider( + ApiService(apiUrl: context.read().apiUrl))), + RepositoryProvider(create: (context) => UserRepository()), + ], + child: MultiBlocProvider( + providers: [ + BlocProvider( create: (context) => WeekScheduleCubit( - weekScheduleRepository: - context.read(), - userRepository: context.read(), - ), - child: const WeekSchedulePage())))), + weekScheduleRepository: WeekScheduleRepository( + apiService: ApiService( + apiUrl: context.read().apiUrl)), + initialDate: DateTime.now(), + userCubit: context.read()), + ), + BlocProvider( + create: (context) => UserCubit( + userRepository: context.read()) + ..fetchUser(), + ), + ], + child: const WeekSchedulePage(), + ), + ))), ]; } diff --git a/frontend/app_student/lib/users/cubit/user_cubit.dart b/frontend/app_student/lib/users/cubit/user_cubit.dart new file mode 100644 index 0000000..0554d9f --- /dev/null +++ b/frontend/app_student/lib/users/cubit/user_cubit.dart @@ -0,0 +1,33 @@ +import 'package:app_student/api/class_groups/models/class_group_model.dart'; +import 'package:app_student/api/users/models/user_model.dart'; +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +import '../../api/users/repositories/user_repository.dart'; + +part 'user_state.dart'; + +class UserCubit extends Cubit { + final UserRepository userRepository; + + UserCubit({required this.userRepository}) : super(UserInitial()); + + Future fetchUser() async { + try { + emit(UserLoading()); + final user = await userRepository.getUser(); + emit(UserLoaded(user)); + } catch (e) { + emit(UserError(e.toString())); + } + } + + Future getCurrentUser() async { + return userRepository.getUser(); + } + + Future saveUserClass(ClassGroupModel classGroup) async { + await userRepository.saveUserClass(classGroup.name.toString()); + emit(UserClassesSelected()); + } +} diff --git a/frontend/app_student/lib/users/cubit/user_state.dart b/frontend/app_student/lib/users/cubit/user_state.dart new file mode 100644 index 0000000..c08adbb --- /dev/null +++ b/frontend/app_student/lib/users/cubit/user_state.dart @@ -0,0 +1,22 @@ +part of 'user_cubit.dart'; + +@immutable +abstract class UserState {} + +class UserInitial extends UserState {} + +class UserLoading extends UserState {} + +class UserLoaded extends UserState { + final UserModel user; + + UserLoaded(this.user); +} + +class UserClassesSelected extends UserState {} + +class UserError extends UserState { + final String message; + + UserError(this.message); +} 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 65b7672..a42eb8b 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 @@ -1,36 +1,62 @@ +import 'package:app_student/api/day_schedule/models/day_schedule_model.dart'; +import 'package:app_student/api/users/models/user_model.dart'; +import 'package:app_student/api/week_schedule/models/week_schedule_model.dart'; import 'package:app_student/api/week_schedule/repositories/week_schedule_repository.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:bloc/bloc.dart'; +import 'package:flutter_bloc/flutter_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; + final UserCubit userCubit; + late DateTime? initialDate; WeekScheduleCubit( - {required this.weekScheduleRepository, required this.userRepository}) + {required this.weekScheduleRepository, + required this.userCubit, + required this.initialDate}) : super(WeekScheduleInitial()); - Future getUser() async { - return await userRepository.getUser(); - } - - Future fetchWeekSchedule(String className) async { + Future fetchWeekSchedule() async { try { + if (isClosed) return; emit(WeekScheduleLoading()); + final user = await userCubit.getCurrentUser(); final weekSchedule = - await weekScheduleRepository.getWeeksSchedule(className); - emit(WeekScheduleLoaded(weekSchedule)); + await weekScheduleRepository.getWeeksSchedule(user.className!); + + final allEvents = + weekSchedule.expand((week) => week.daySchedules).toList(); + + initialDate ??= DateTime.now(); + + final todayIndex = allEvents.indexWhere((daySchedule) => + daySchedule.date.day == initialDate!.day && + daySchedule.date.month == initialDate!.month && + daySchedule.date.year == initialDate!.year); + + emit(WeekScheduleLoaded(weekSchedule, todayIndex, allEvents, user)); } catch (e) { emit(WeekScheduleError(e.toString())); } } void fetchUserAndSchedule() async { - final user = await getUser(); - fetchWeekSchedule(user.className); + fetchWeekSchedule(); + } + + void updateTodayIndex(int index) { + if (state is WeekScheduleLoaded) { + emit((state as WeekScheduleLoaded).copyWith(todayIndex: index)); + } + } + + void changeDate(DateTime newDate) { + initialDate = newDate; + emit(WeekScheduleDateChanged(newDate)); + fetchUserAndSchedule(); } } diff --git a/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart b/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart index cea2f6f..6a21522 100644 --- a/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart +++ b/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart @@ -8,9 +8,33 @@ class WeekScheduleInitial extends WeekScheduleState {} class WeekScheduleLoading extends WeekScheduleState {} class WeekScheduleLoaded extends WeekScheduleState { - final List weekSchedule; + final List weekSchedule; + final int todayIndex; + final List allDaySchedules; + final UserModel user; // Ajout du champ UserModel - WeekScheduleLoaded(this.weekSchedule); + WeekScheduleLoaded(this.weekSchedule, this.todayIndex, this.allDaySchedules, + this.user); // Ajout du UserModel au constructeur + + WeekScheduleLoaded copyWith({ + List? weekSchedule, + int? todayIndex, + List? allDaySchedules, + UserModel? user, // Ajout du UserModel à la méthode copyWith + }) { + return WeekScheduleLoaded( + weekSchedule ?? this.weekSchedule, + todayIndex ?? this.todayIndex, + allDaySchedules ?? this.allDaySchedules, + user ?? this.user, // Utilisation du UserModel dans la méthode copyWith + ); + } +} + +class WeekScheduleDateChanged extends WeekScheduleState { + final DateTime date; + + WeekScheduleDateChanged(this.date); } class WeekScheduleError extends WeekScheduleState { 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 57ca24d..83a6229 100644 --- a/frontend/app_student/lib/week_schedule/views/week_schedule.dart +++ b/frontend/app_student/lib/week_schedule/views/week_schedule.dart @@ -1,14 +1,17 @@ import 'package:app_student/api/week_schedule/repositories/week_schedule_repository.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:app_student/week_schedule/cubit/week_schedule_cubit.dart'; import 'package:app_student/week_schedule/views/widgets/day_schedule_widget.dart'; -import 'package:app_student/week_schedule/views/widgets/components/custom_appbar.dart'; +import 'package:app_student/week_schedule/views/widgets/components/app_bar_week_schedule.dart'; 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}); + final DateTime? initialDate; + + const WeekSchedulePage({super.key, this.initialDate}); @override Widget build(BuildContext context) { @@ -16,47 +19,51 @@ class WeekSchedulePage extends StatelessWidget { RepositoryProvider.of(context); final userRepository = RepositoryProvider.of(context); final weekScheduleCubit = WeekScheduleCubit( + userCubit: context.read(), weekScheduleRepository: weekScheduleRepository, - userRepository: userRepository); + initialDate: initialDate); return BlocProvider( - create: (context) => weekScheduleCubit..fetchUserAndSchedule(), - child: Scaffold( - appBar: const AppBarWeekSchedule(), - body: BlocBuilder( - builder: (context, state) { - if (state is WeekScheduleLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is WeekScheduleLoaded) { - return Padding( - padding: const EdgeInsets.only( - top: 30.0), // Ajoutez un espacement en haut - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: PageView.builder( - itemCount: state.weekSchedule.length, // Nombre de semaines - itemBuilder: (context, weekIndex) { - final week = state.weekSchedule[weekIndex]; - return PageView.builder( - itemCount: week.daySchedules - .length, // Nombre de jours dans la semaine - itemBuilder: (context, dayIndex) { - final daySchedule = week.daySchedules[dayIndex]; + create: (context) => weekScheduleCubit..fetchUserAndSchedule(), + child: BlocProvider( + create: (context) => + UserCubit(userRepository: userRepository)..fetchUser(), + child: Scaffold( + appBar: const AppBarWeekSchedule(), + body: BlocBuilder( + builder: (context, state) { + if (state is WeekScheduleLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is WeekScheduleLoaded) { + final allEvents = state.weekSchedule + .expand((week) => week.daySchedules) + .toList(); + + return Padding( + padding: const EdgeInsets.only(top: 30.0), + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: PageView.builder( + controller: PageController( + initialPage: + state.todayIndex != -1 ? state.todayIndex : 0, + ), + itemCount: allEvents.length, + itemBuilder: (context, index) { + final daySchedule = allEvents[index]; return DayScheduleWidget(daySchedule: daySchedule); }, - ); - }, - ), - ), - ); - } else if (state is WeekScheduleError) { - return Center(child: Text(state.message)); - } else { - return const SizedBox.shrink(); - } - }, - ), - ), - ); + ), + ), + ); + } else if (state is WeekScheduleError) { + return Center(child: Text(state.message)); + } else { + return const SizedBox.shrink(); + } + }, + ), + ), + )); } } diff --git a/frontend/app_student/lib/week_schedule/views/widgets/components/app_bar_week_schedule.dart b/frontend/app_student/lib/week_schedule/views/widgets/components/app_bar_week_schedule.dart new file mode 100644 index 0000000..811b4a9 --- /dev/null +++ b/frontend/app_student/lib/week_schedule/views/widgets/components/app_bar_week_schedule.dart @@ -0,0 +1,56 @@ +import 'package:app_student/users/cubit/user_cubit.dart'; +import 'package:app_student/week_schedule/views/widgets/components/datepicker_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AppBarWeekSchedule extends StatelessWidget + implements PreferredSizeWidget { + const AppBarWeekSchedule({super.key}); + + @override + Widget build(BuildContext context) { + final userState = context.watch().state; + String className = ''; + if (userState is UserLoaded) { + className = userState.user.entity.className ?? ''; + } + return AppBar( + backgroundColor: const Color(0xFF005067), + title: const SizedBox.shrink(), // Make the title empty + flexibleSpace: Stack( + alignment: Alignment.center, + children: [ + ClipRect( + child: Center( + child: Opacity( + opacity: 0.5, + child: Transform.scale( + scale: 3.0, // Adjust the scale factor to zoom the image + child: Image.asset( + 'assets/images/3il-icon-white.png', + fit: BoxFit.cover, + ), + ), + ), + ), + ), + Text( + className, + style: const TextStyle( + color: Colors.white, + fontSize: 25.0, + fontWeight: FontWeight.bold, + ), + ), + const Positioned( + right: 0, + child: DatePickerButton(), + ), + ], + ), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(70.0); +} diff --git a/frontend/app_student/lib/week_schedule/views/widgets/components/custom_appbar.dart b/frontend/app_student/lib/week_schedule/views/widgets/components/custom_appbar.dart deleted file mode 100644 index bb524d4..0000000 --- a/frontend/app_student/lib/week_schedule/views/widgets/components/custom_appbar.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:app_student/week_schedule/views/widgets/components/datepicker_button.dart'; -import 'package:flutter/material.dart'; - -class AppBarWeekSchedule extends StatelessWidget - implements PreferredSizeWidget { - const AppBarWeekSchedule({super.key}); - - @override - Widget build(BuildContext context) { - return AppBar( - backgroundColor: const Color(0xFF005067), - leading: Image.asset('assets/images/3il-logo.jpg'), - title: const Center( - child: Text('Classe Name', style: TextStyle(color: Colors.white))), - actions: const [ - DatePickerButton(), - ], - ); - } - - @override - Size get preferredSize => const Size.fromHeight(70.0); -} diff --git a/frontend/app_student/lib/week_schedule/views/widgets/components/datepicker_button.dart b/frontend/app_student/lib/week_schedule/views/widgets/components/datepicker_button.dart index 73e05d3..f8fb5c8 100644 --- a/frontend/app_student/lib/week_schedule/views/widgets/components/datepicker_button.dart +++ b/frontend/app_student/lib/week_schedule/views/widgets/components/datepicker_button.dart @@ -1,21 +1,60 @@ +import 'package:app_student/week_schedule/cubit/week_schedule_cubit.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; class DatePickerButton extends StatelessWidget { const DatePickerButton({super.key}); + Future navigateToDate( + BuildContext context, WeekScheduleCubit cubit, DateTime date) async { + final index = (cubit.state as WeekScheduleLoaded) + .allDaySchedules + .indexWhere((event) => + DateTime(event.date.year, event.date.month, event.date.day) == + DateTime(date.year, date.month, date.day)); + if (index != -1) { + cubit.changeDate(date); + context.go('/schedule'); // Replace with the appropriate navigation logic + } + } + + Future selectDate( + BuildContext context, WeekScheduleCubit cubit, DateTime today) async { + return await showDatePicker( + context: context, + initialDate: cubit.state is WeekScheduleLoaded + ? (cubit.state as WeekScheduleLoaded) + .allDaySchedules[(cubit.state as WeekScheduleLoaded).todayIndex] + .date + : today, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + selectableDayPredicate: (date) { + return cubit.state is WeekScheduleLoaded && + (cubit.state as WeekScheduleLoaded).allDaySchedules.any((event) => + DateTime(event.date.year, event.date.month, event.date.day) == + DateTime(date.year, date.month, date.day)); + }, + ); + } + @override Widget build(BuildContext context) { + final cubit = context.read(); return IconButton( - icon: const Icon(Icons.calendar_month, color: Colors.white), + icon: const Icon( + Icons.calendar_month, + color: Colors.white, + size: 30, + ), onPressed: () async { - final date = await showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime(2000), - lastDate: DateTime(2100), - ); + final today = DateTime.now(); + final date = await selectDate(context, cubit, today); if (date != null) { - // Utilisez la date sélectionnée + if (context.mounted) { + await navigateToDate(context, cubit, date); // Await the navigation + } } }, ); diff --git a/frontend/app_student/lib/week_schedule/views/widgets/day_schedule_widget.dart b/frontend/app_student/lib/week_schedule/views/widgets/day_schedule_widget.dart index bb3667a..f22a2bf 100644 --- a/frontend/app_student/lib/week_schedule/views/widgets/day_schedule_widget.dart +++ b/frontend/app_student/lib/week_schedule/views/widgets/day_schedule_widget.dart @@ -1,6 +1,7 @@ import 'package:app_student/api/day_schedule/models/day_schedule_model.dart'; import 'package:app_student/week_schedule/views/widgets/events/event_details.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class DayScheduleWidget extends StatelessWidget { final DayScheduleModel daySchedule; @@ -9,11 +10,27 @@ class DayScheduleWidget extends StatelessWidget { @override Widget build(BuildContext context) { + String formattedDate = + DateFormat('EEEE dd MMMM yyyy', 'fr_FR').format(daySchedule.date); + String capitalizedDate = + formattedDate[0].toUpperCase() + formattedDate.substring(1); return SingleChildScrollView( child: Column( - children: daySchedule.events.map((event) { - return EventDetails(event: event); - }).toList(), + children: [ + Center( + child: Text( + capitalizedDate, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(height: 30), + ...daySchedule.events.map((event) { + return EventDetails(event: event); + }), + ], ), ); } diff --git a/frontend/app_student/lib/week_schedule/views/widgets/events/event_card.dart b/frontend/app_student/lib/week_schedule/views/widgets/events/event_card.dart index c800d90..90eb2d2 100644 --- a/frontend/app_student/lib/week_schedule/views/widgets/events/event_card.dart +++ b/frontend/app_student/lib/week_schedule/views/widgets/events/event_card.dart @@ -1,6 +1,7 @@ import 'package:app_student/api/events/models/event_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class EventCard extends StatelessWidget { const EventCard({ @@ -23,8 +24,7 @@ class EventCard extends StatelessWidget { ), ), ), - width: 300, - height: 90, + height: 110, child: Padding( padding: const EdgeInsets.only(left: 20.0, right: 20, bottom: 3, top: 3), @@ -33,46 +33,32 @@ class EventCard extends StatelessWidget { mainAxisAlignment: event.creneau == 3 ? MainAxisAlignment.center : MainAxisAlignment.spaceEvenly, - children: event.creneau == 3 - ? [ - Center( - child: ColorFiltered( - colorFilter: const ColorFilter.mode( - Colors.white, BlendMode.srcIn), - child: SvgPicture.asset( - 'assets/images/eating.svg', - width: 40, - height: 40, - ), - ), - ) - ] - : [ - const Text('1h30', - style: TextStyle(color: Colors.white, fontSize: 12)), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(event.activite, - style: const TextStyle( - fontSize: 18, fontWeight: FontWeight.bold)), - ColorFiltered( - colorFilter: const ColorFilter.mode( - Colors.white, BlendMode.srcIn), - child: SvgPicture.asset( - event.visio - ? 'assets/images/teams.svg' - : 'assets/images/school.svg', - width: 40, - height: 40, - ), - ), - ], + children: [ + Text(AppLocalizations.of(context)!.eventDuration, + style: const TextStyle(color: Colors.white, fontSize: 12)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(event.activite, + style: const TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), + ColorFiltered( + colorFilter: + const ColorFilter.mode(Colors.white, BlendMode.srcIn), + child: SvgPicture.asset( + event.visio + ? 'assets/images/teams.svg' + : 'assets/images/school.svg', + width: 50, + height: 50, ), - Text('Salle ${event.salle}', - style: const TextStyle( - fontSize: 15, fontWeight: FontWeight.bold)), - ], + ), + ], + ), + Text('${AppLocalizations.of(context)!.roomLabel} ${event.salle}', + style: const TextStyle( + fontSize: 15, fontWeight: FontWeight.bold)), + ], ), ), ), diff --git a/frontend/app_student/lib/week_schedule/views/widgets/events/event_details.dart b/frontend/app_student/lib/week_schedule/views/widgets/events/event_details.dart index d61ad2d..d05ce55 100644 --- a/frontend/app_student/lib/week_schedule/views/widgets/events/event_details.dart +++ b/frontend/app_student/lib/week_schedule/views/widgets/events/event_details.dart @@ -1,6 +1,6 @@ import 'package:app_student/api/events/models/event_model.dart'; -import 'package:app_student/week_schedule/views/widgets/events/event_card.dart'; import 'package:app_student/week_schedule/views/widgets/events/event_hours.dart'; +import 'package:app_student/week_schedule/views/widgets/events/event_info.dart'; import 'package:flutter/material.dart'; class EventDetails extends StatelessWidget { @@ -10,12 +10,20 @@ class EventDetails extends StatelessWidget { @override Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - EventHours(event: event), - EventCard(event: event), - ], + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + EventHours(event: event), + SizedBox( + width: + constraints.maxWidth - 50, // Subtract the width of EventHours + child: EventInfo(event: event), + ), + ], + ); + }, ); } } diff --git a/frontend/app_student/lib/week_schedule/views/widgets/events/event_empty_card.dart b/frontend/app_student/lib/week_schedule/views/widgets/events/event_empty_card.dart new file mode 100644 index 0000000..1da7d49 --- /dev/null +++ b/frontend/app_student/lib/week_schedule/views/widgets/events/event_empty_card.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class EventEmptyCard extends StatelessWidget { + const EventEmptyCard({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Card( + color: Colors.grey.shade200, + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: Colors.grey, + width: 10.0, + ), + ), + ), + height: 110, + ), + ); + } +} diff --git a/frontend/app_student/lib/week_schedule/views/widgets/events/event_info.dart b/frontend/app_student/lib/week_schedule/views/widgets/events/event_info.dart new file mode 100644 index 0000000..f9bc8cf --- /dev/null +++ b/frontend/app_student/lib/week_schedule/views/widgets/events/event_info.dart @@ -0,0 +1,24 @@ +import 'package:app_student/api/events/models/event_model.dart'; +import 'package:flutter/material.dart'; +import 'event_card.dart'; +import 'event_empty_card.dart'; +import 'event_meal_card.dart'; + +class EventInfo extends StatelessWidget { + const EventInfo({ + super.key, + required this.event, + }); + + final EventModel event; + + @override + Widget build(BuildContext context) { + if (event.activite == 'Pas cours' && event.creneau != 3) { + return const EventEmptyCard(); + } + return event.repas == true + ? const EventMealCard() + : EventCard(event: event); + } +} diff --git a/frontend/app_student/lib/week_schedule/views/widgets/events/event_meal_card.dart b/frontend/app_student/lib/week_schedule/views/widgets/events/event_meal_card.dart new file mode 100644 index 0000000..67e4898 --- /dev/null +++ b/frontend/app_student/lib/week_schedule/views/widgets/events/event_meal_card.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class EventMealCard extends StatelessWidget { + const EventMealCard({super.key}); + + @override + Widget build(BuildContext context) { + return Card( + color: const Color(0xFF007A8D), + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: Color(0xFF005067), + width: 10.0, + ), + ), + ), + height: 90, + child: Padding( + padding: + const EdgeInsets.only(left: 20.0, right: 20, bottom: 3, top: 3), + child: Center( + child: ColorFiltered( + colorFilter: + const ColorFilter.mode(Colors.white, BlendMode.srcIn), + child: SvgPicture.asset( + 'assets/images/eating.svg', + width: 50, + height: 50, + ), + ), + ), + ), + ), + ); + } +} diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index 5195719..66a657b 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -43,10 +43,11 @@ dependencies: meta: ^1.11.0 flutter_svg: ^2.0.10+1 go_router: ^13.2.0 + intl: ^0.18.1 + flutter_dotenv: ^5.1.0 flutter_localizations: sdk: flutter - intl: any - flutter_dotenv: ^5.1.0 + dev_dependencies: flutter_test: @@ -65,6 +66,7 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: generate: true + # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. @@ -107,5 +109,6 @@ flutter: fonts: - asset: assets/fonts/arial.ttf + # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages