diff --git a/backend/src/Service/TimetableService.php b/backend/src/Service/TimetableService.php index 218fd37..7749dee 100644 --- a/backend/src/Service/TimetableService.php +++ b/backend/src/Service/TimetableService.php @@ -41,7 +41,7 @@ public function getXmlFile(string $classParam): ?string if ($class->getName() === $classParam) { $encodedClassFile = str_replace(' ', '%20', $class->getFile()); - return $this->schedule_url.$encodedClassFile; + return $this->schedule_url . $encodedClassFile; } } @@ -73,6 +73,13 @@ public function parseData(array $parsedData): array $changes = false; + $weeksTest = $parsedData['GROUPE']['PLAGES']['SEMAINE']; + // Si 'weeksTest' contient une seule semaine, convertissez-la en tableau multidimensionnel + + if (!isset($weeksTest[0])) { + $parsedData['GROUPE']['PLAGES']['SEMAINE'] = [$weeksTest]; + } + foreach ($parsedData['GROUPE']['PLAGES']['SEMAINE'] as $week) { $weekSchedule = new WeekSchedule(); $weekSchedule->setId($week['SemId']); @@ -125,26 +132,28 @@ public function parseData(array $parsedData): array if ($existingEvent) { // Si l'événement existe déjà, fusionnez les modifications - if ($existingEvent->getActivite() !== $event->getActivite() + if ( + $existingEvent->getActivite() !== $event->getActivite() || $existingEvent->getCouleur() !== $event->getCouleur() || $existingEvent->getSalle() !== $event->getSalle() || $existingEvent->isRepas() !== $event->isRepas() || $existingEvent->isVisio() !== $event->isVisio() - || $existingEvent->getEval() !== $event->getEval()) { - - - (new Logger())->info('Existing event: '.$existingEvent->getActivite()); - (new Logger())->info('New event: '.$event->getActivite()); - (new Logger())->info('Existing event: '.$existingEvent->getCouleur()); - (new Logger())->info('New event: '.$event->getCouleur()); - (new Logger())->info('Existing event: '.$existingEvent->getSalle()); - (new Logger())->info('New event: '.$event->getSalle()); - (new Logger())->info('Existing event: '.$existingEvent->isRepas()); - (new Logger())->info('New event: '.$event->isRepas()); - (new Logger())->info('Existing event: '.$existingEvent->isVisio()); - (new Logger())->info('New event: '.$event->isVisio()); - (new Logger())->info('Existing event: '.$existingEvent->getEval()); - (new Logger())->info('New event: '.$event->getEval()); + || $existingEvent->getEval() !== $event->getEval() + ) { + + + (new Logger())->info('Existing event: ' . $existingEvent->getActivite()); + (new Logger())->info('New event: ' . $event->getActivite()); + (new Logger())->info('Existing event: ' . $existingEvent->getCouleur()); + (new Logger())->info('New event: ' . $event->getCouleur()); + (new Logger())->info('Existing event: ' . $existingEvent->getSalle()); + (new Logger())->info('New event: ' . $event->getSalle()); + (new Logger())->info('Existing event: ' . $existingEvent->isRepas()); + (new Logger())->info('New event: ' . $event->isRepas()); + (new Logger())->info('Existing event: ' . $existingEvent->isVisio()); + (new Logger())->info('New event: ' . $event->isVisio()); + (new Logger())->info('Existing event: ' . $existingEvent->getEval()); + (new Logger())->info('New event: ' . $event->getEval()); $existingEvent->setActivite($event->getActivite()); $existingEvent->setCouleur($event->getCouleur()); diff --git a/frontend/app_student/.fvm/flutter_sdk b/frontend/app_student/.fvm/flutter_sdk new file mode 120000 index 0000000..bf809de --- /dev/null +++ b/frontend/app_student/.fvm/flutter_sdk @@ -0,0 +1 @@ +C:/Users/Jules/fvm/versions/3.19.2 \ No newline at end of file diff --git a/frontend/app_student/.fvm/release b/frontend/app_student/.fvm/release new file mode 100644 index 0000000..0612d19 --- /dev/null +++ b/frontend/app_student/.fvm/release @@ -0,0 +1 @@ +3.19.2 \ No newline at end of file diff --git a/frontend/app_student/.fvm/versions/3.19.2 b/frontend/app_student/.fvm/versions/3.19.2 new file mode 120000 index 0000000..bf809de --- /dev/null +++ b/frontend/app_student/.fvm/versions/3.19.2 @@ -0,0 +1 @@ +C:/Users/Jules/fvm/versions/3.19.2 \ No newline at end of file diff --git a/frontend/app_student/android/app/build.gradle b/frontend/app_student/android/app/build.gradle index 76cd5f8..d3e1b9f 100644 --- a/frontend/app_student/android/app/build.gradle +++ b/frontend/app_student/android/app/build.gradle @@ -83,4 +83,4 @@ flutter { } dependencies { -} +} \ No newline at end of file diff --git a/frontend/app_student/android/app/src/main/AndroidManifest.xml b/frontend/app_student/android/app/src/main/AndroidManifest.xml index 80c3a8f..137f378 100644 --- a/frontend/app_student/android/app/src/main/AndroidManifest.xml +++ b/frontend/app_student/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,9 @@ + android:icon="@mipmap/ic_launcher" + android:enableOnBackInvokedCallback="true"> + login(String username, String password) async { - final response = await http.post( - Uri.parse('${apiService.apiUrl}/api/student/login'), - body: {'username': username, 'password': password}, - ); - - if (response.statusCode == 200) { - final data = jsonDecode(response.body); - final studentId = (data['studentId']); - Global.setStudentId(studentId); - return studentId; - } else { - throw Exception('Failed to login'); - } - } - - Future getMarks(int studentId) async { - final response = await http.get( - Uri.parse('${apiService.apiUrl}/api/student/marks/$studentId'), - ); - - if (response.statusCode == 200) { - return _savePdfFile(response.bodyBytes, 'marks.pdf'); - } else { - throw Exception('Failed to get marks'); - } - } - - Future getAbsences(int studentId) async { - final response = await http.get( - Uri.parse('${apiService.apiUrl}/api/student/absences/$studentId'), - ); - - if (response.statusCode == 200) { - return _savePdfFile(response.bodyBytes, 'absences.pdf'); - } else { - throw Exception('Failed to get absences'); - } - } - - Future _savePdfFile(List bytes, String filename) async { - final directory = await getApplicationDocumentsDirectory(); - final path = directory.path; - final file = File('$path/$filename'); - - // Écrire les bytes de la réponse dans le fichier - await file.writeAsBytes(bytes); - - return file; - } -} diff --git a/frontend/app_student/lib/api/documents/entities/document_entity.dart b/frontend/app_student/lib/api/documents/entities/document_entity.dart new file mode 100644 index 0000000..e8bc267 --- /dev/null +++ b/frontend/app_student/lib/api/documents/entities/document_entity.dart @@ -0,0 +1,20 @@ +import 'dart:io'; + +class DocumentEntity { + final String title; + final File? file; + + DocumentEntity({required this.title, required this.file}); + + Map toJson() => { + 'title': title, + 'file': file!.path, + }; + + factory DocumentEntity.fromJson(Map json) { + return DocumentEntity( + title: json['title'], + file: File(json['file']), + ); + } +} diff --git a/frontend/app_student/lib/api/documents/models/document_model.dart b/frontend/app_student/lib/api/documents/models/document_model.dart new file mode 100644 index 0000000..6a444b5 --- /dev/null +++ b/frontend/app_student/lib/api/documents/models/document_model.dart @@ -0,0 +1,30 @@ +import 'dart:io'; +import 'package:app_student/api/documents/entities/document_entity.dart'; + +class DocumentModel { + final DocumentEntity entity; + + DocumentModel({required this.entity}); + + String get title => entity.title; + + File? get file => entity.file; + + factory DocumentModel.fromEntity(DocumentEntity entity) { + return DocumentModel( + entity: entity, + ); + } + + DocumentEntity toEntity() { + return DocumentEntity( + title: title, + file: file, + ); + } + + @override + String toString() { + return 'DocumentModel: {title: $title, file: ${file?.path}}'; + } +} 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 38d03a7..ffcecbe 100644 --- a/frontend/app_student/lib/api/users/entities/user_entity.dart +++ b/frontend/app_student/lib/api/users/entities/user_entity.dart @@ -1,20 +1,40 @@ -import 'dart:io'; +import '../../documents/entities/document_entity.dart'; class UserEntity { - final String? ine; - final String firstName; - final DateTime? birthDate; - final String? className; - final int? studentId; - final File? marksFile; - final File? absencesFile; + String? ine; + String? firstName; + DateTime? birthDate; + String? className; + int? studentId; + List? documents; UserEntity( - {required this.ine, - required this.firstName, - required this.birthDate, + {this.ine, + this.firstName, + this.birthDate, this.className, this.studentId, - this.marksFile, - this.absencesFile}); + this.documents}); + + Map toJson() => { + 'firstName': firstName, + 'className': className, + 'ine': ine, + 'birthDate': birthDate?.toIso8601String(), + 'studentId': studentId, + 'documents': documents?.map((doc) => doc.toJson()).toList(), + }; + + factory UserEntity.fromJson(Map json) => UserEntity( + firstName: json['firstName'], + className: json['className'], + ine: json['ine'], + birthDate: json['birthDate'] != null + ? DateTime.parse(json['birthDate']) + : null, + studentId: json['studentId'], + documents: (json['documents'] as List?) + ?.map((doc) => DocumentEntity.fromJson(doc)) + .toList(), + ); } 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 35a925d..fc647c1 100644 --- a/frontend/app_student/lib/api/users/models/user_model.dart +++ b/frontend/app_student/lib/api/users/models/user_model.dart @@ -1,25 +1,93 @@ -import 'dart:io'; - +import 'package:app_student/api/documents/models/document_model.dart'; import 'package:app_student/api/users/entities/user_entity.dart'; class UserModel { - final UserEntity entity; + UserEntity entity; UserModel({required this.entity}); - String get firstName => entity.firstName; + String? get firstName => entity.firstName; - String get name => entity.firstName; + set firstName(String? value) => entity.firstName = value; DateTime? get birthDate => entity.birthDate; + set birthDate(DateTime? value) => entity.birthDate = value; + String? get className => entity.className; + set className(String? value) => entity.className = value; + String? get ine => entity.ine; + set ine(String? value) => entity.ine = value; + int? get studentId => entity.studentId; - File? get marksFile => entity.marksFile; + set studentId(int? value) => entity.studentId = value; + + List? get documents => entity.documents + ?.map((documentEntity) => DocumentModel.fromEntity(documentEntity)) + .toList(); + + set documents(List? value) { + entity.documents = value?.map((model) => model.toEntity()).toList(); + } + + Map toJson() => { + 'entity': entity.toJson(), + }; + + factory UserModel.fromJson(Map json) => UserModel( + entity: json['entity'] != null + ? UserEntity.fromJson(json['entity']) + : UserEntity(), + ); + + @override + String toString() { + return 'User : {firstName: $firstName, birthDate: $birthDate, className: $className, ine: $ine, studentId: $studentId, documents: ${documents?.map((doc) => doc.toString()).join(', ')}}'; + } + + bool get isEmpty { + return firstName == null || firstName!.isEmpty; + } + + bool get hasFirstName { + return firstName != null && firstName!.isNotEmpty; + } + + bool get hasBirthDate { + return birthDate != null; + } + + bool get hasClassName { + return className != null && className!.isNotEmpty; + } + + bool get hasIne { + return ine != null && ine!.isNotEmpty; + } + + bool get hasStudentId { + return studentId != null; + } + + bool get hasDocuments { + return documents != null && documents!.isNotEmpty; + } - File? get absencesFile => entity.absencesFile; + static UserModel create(String firstName, String? className, String? ine, + DateTime? birthDate, int? studentId, List? documents) { + return UserModel( + entity: UserEntity( + firstName: firstName, + className: className, + ine: ine, + birthDate: birthDate, + studentId: studentId, + documents: documents?.map((doc) => doc.toEntity()).toList(), + ), + ); + } } diff --git a/frontend/app_student/lib/api/users/repositories/user_repository.dart b/frontend/app_student/lib/api/users/repositories/user_repository.dart index 4e7a058..6d45a36 100644 --- a/frontend/app_student/lib/api/users/repositories/user_repository.dart +++ b/frontend/app_student/lib/api/users/repositories/user_repository.dart @@ -4,8 +4,8 @@ import 'package:app_student/api/users/models/user_model.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../../utils/global.dart'; import '../../api_service.dart'; +import '../../documents/entities/document_entity.dart'; import '../entities/user_entity.dart'; import 'package:http/http.dart' as http; @@ -14,87 +14,28 @@ class UserRepository { UserRepository({required this.apiService}); + Future createUser(UserModel user) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String userJson = jsonEncode(user.toJson()); + await prefs.setString('user', userJson); + } + Future getUser() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); + String? userJson = prefs.getString('user'); - String? name = prefs.getString('name'); - String? className = prefs.getString('className'); - String? ine = prefs.getString('ine'); - String? birthDate = prefs.getString('birthDate'); - int? studentId = prefs.getInt('studentId'); + if (userJson != null) { + Map userMap = jsonDecode(userJson); - File marksFile = File(''); - File absencesFile = File(''); - - if (studentId != null) { - marksFile = await getMarks(studentId); - absencesFile = await getAbsences(studentId); - } - - if (name == null) { - throw Exception('User name not found'); - } - - DateTime bd = DateTime.now(); - if (birthDate != null) { - bd = DateFormat('dd/MM/yyyy').parse(birthDate); - } - - return UserModel( - entity: UserEntity( - firstName: name, - className: className, - ine: ine, - birthDate: bd, - studentId: studentId, - marksFile: marksFile, - absencesFile: absencesFile), - ); - } - - Future saveUserDetails(String name, String? className, - {String? ine, String? birthDate}) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - UserModel( - entity: UserEntity( - firstName: name, - className: className, - ine: ine, - birthDate: birthDate != null ? DateTime.parse(birthDate) : null, - ), - ); - await prefs.setString('name', name); - if (className != null) { - await prefs.setString('className', className); - } - if (ine != null) { - await prefs.setString('ine', ine); - } - if (birthDate != null) { - await prefs.setString('birthDate', birthDate); + return UserModel.fromJson(userMap); + } else { + throw Exception('No user found in cache'); } } - 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); - } - Future delete() async { SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.remove('ine'); - await prefs.remove('name'); - await prefs.remove('birthDate'); - await prefs.remove('className'); - await prefs.remove('studentId'); + await prefs.remove('user'); } Future clearClass() async { @@ -102,54 +43,72 @@ class UserRepository { await prefs.remove('className'); } - Future login(String username, String password) async { + Future login(String ine, String birthDate) async { final response = await http.post( Uri.parse('${apiService.apiUrl}/api/student/login'), - body: {'username': username, 'password': password}, + body: {'username': ine, 'password': birthDate}, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); final studentId = (data['studentId']); - Global.setStudentId(studentId); - return studentId; + + List documents = []; + if (studentId != null) { + documents = await downloadStudentFiles(studentId); + } + + // Get the current user from the cache + UserModel user; + try { + user = await getUser(); + } catch (e) { + user = UserModel(entity: UserEntity()); + } + + // Update the user data + user.entity.firstName = user.entity.firstName; + user.entity.ine = ine; + DateFormat format = DateFormat('dd/MM/yyyy'); + user.entity.birthDate = format.parse(birthDate); + user.entity.className = user.entity.className; + user.entity.studentId = studentId ?? ''; + user.entity.documents = documents; + + await createUser(user); + + return user; } else { throw Exception('Failed to login'); } } - Future getMarks(int studentId) async { - final response = await http.get( - Uri.parse('${apiService.apiUrl}/api/student/marks/$studentId'), - ); + Future downloadFile(String url, String filename) async { + final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { - return _savePdfFile(response.bodyBytes, 'marks.pdf'); - } else { - throw Exception('Failed to get marks'); - } - } + final directory = await getTemporaryDirectory(); + final file = File('${directory.path}/$filename'); - Future getAbsences(int studentId) async { - final response = await http.get( - Uri.parse('${apiService.apiUrl}/api/student/absences/$studentId'), - ); + await file.writeAsBytes(response.bodyBytes); - if (response.statusCode == 200) { - return _savePdfFile(response.bodyBytes, 'absences.pdf'); + return file; } else { - throw Exception('Failed to get absences'); + throw Exception('Failed to download file'); } } - Future _savePdfFile(List bytes, String filename) async { - final directory = await getApplicationDocumentsDirectory(); - final path = directory.path; - final file = File('$path/$filename'); + Future> downloadStudentFiles(int studentId) async { + final marksFile = await downloadFile( + '${apiService.apiUrl}/api/student/marks/$studentId', 'notes.pdf'); + final absencesFile = await downloadFile( + '${apiService.apiUrl}/api/student/absences/$studentId', 'absences.pdf'); - // Écrire les bytes de la réponse dans le fichier - await file.writeAsBytes(bytes); + final marksDocument = + DocumentEntity(title: 'Relevé de notes', file: marksFile); + final absencesDocument = + DocumentEntity(title: "Relevé d'absences", file: absencesFile); - return file; + return [marksDocument, absencesDocument]; } } 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 250275e..91ed844 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 @@ -1,4 +1,3 @@ -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/users/cubit/user_cubit.dart'; @@ -7,7 +6,6 @@ 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/repositories/user_repository.dart'; import '../../shared_components/app_bar.dart'; import '../../shared_components/header_subtitle.dart'; import '../../shared_components/header_title.dart'; @@ -19,57 +17,53 @@ class ClassGroupPage extends StatelessWidget { @override Widget build(BuildContext context) { - final classRepository = - RepositoryProvider.of(context); - final userRepository = RepositoryProvider.of(context); - final classCubit = ClassGroupCubit(classRepository: classRepository) - ..fetchClasses(); - final userCubit = UserCubit(userRepository: userRepository)..fetchUser(); - - return MultiBlocProvider( - providers: [ - BlocProvider(create: (context) => classCubit), - BlocProvider(create: (context) => userCubit), - ], - child: CustomLayout( - appBar: const CustomAppBar(), - body: BlocBuilder( - builder: (context, userState) { - if (userState is UserClassesSelected) { - WidgetsBinding.instance.addPostFrameCallback((_) { - context.go('/schedule'); - }); - return const Center(child: CircularProgressIndicator()); - } else if (userState is UserLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (userState is UserWithoutClass) { - final user = userState.user; - return BlocBuilder( - builder: (context, classState) { - if (classState is ClassGroupLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (classState is ClassGroupLoaded) { - return Column( - children: [ - HeaderTitle(AppLocalizations.of(context)! - .classSelectionTitle(user.name)), - HeaderSubtitle(AppLocalizations.of(context)! - .classSelectionSubtitle), - CardList(classesList: classState.classes), - ], - ); - } else if (classState is ClassGroupError) { - return const NetworkError(); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ), + return CustomLayout( + appBar: const CustomAppBar(), + body: BlocBuilder( + builder: (context, userState) { + if (userState is UserInitial) { + return const Center(child: CircularProgressIndicator()); + } else if (userState is UserError) { + return const NetworkError(); + } else if (userState is UserLoggedIn) { + WidgetsBinding.instance.addPostFrameCallback((_) { + context.go('/school_space'); + }); + return const Center(child: CircularProgressIndicator()); + } else if (userState is UserWihtoutLink) { + WidgetsBinding.instance.addPostFrameCallback((_) { + context.go('/schedule'); + }); + return const Center(child: CircularProgressIndicator()); + } else if (userState is UserLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (userState is UserWithoutClass) { + final user = userState.user; + return BlocBuilder( + builder: (context, classState) { + if (classState is ClassGroupLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (classState is ClassGroupLoaded) { + return Column( + children: [ + HeaderTitle(AppLocalizations.of(context)! + .classSelectionTitle(user.firstName!)), + HeaderSubtitle( + AppLocalizations.of(context)!.classSelectionSubtitle), + CardList(classesList: classState.classes), + ], + ); + } else if (classState is ClassGroupError) { + return const NetworkError(); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); + } else { + return const Text('error'); + } + }, ), ); } diff --git a/frontend/app_student/lib/login/cubit/login_cubit.dart b/frontend/app_student/lib/login/cubit/login_cubit.dart index 0d7a651..35d0a9f 100644 --- a/frontend/app_student/lib/login/cubit/login_cubit.dart +++ b/frontend/app_student/lib/login/cubit/login_cubit.dart @@ -2,13 +2,14 @@ import 'package:app_student/api/users/repositories/user_repository.dart'; import 'package:bloc/bloc.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:meta/meta.dart'; +import '../../api/users/models/user_model.dart'; part 'login_state.dart'; class LoginCubit extends Cubit { final UserRepository userRepository; - LoginCubit(this.userRepository) : super(LoginInitial()) { + LoginCubit({required this.userRepository}) : super(LoginInitial()) { checkUserAuthentication(); } @@ -24,6 +25,7 @@ class LoginCubit extends Cubit { emit(LoginFieldError()); return; } + final notificationSettings = await FirebaseMessaging.instance .requestPermission( provisional: true, @@ -37,15 +39,16 @@ class LoginCubit extends Cubit { await FirebaseMessaging.instance.getToken(); } - await userRepository.saveUserDetails(name, '', ine: null, birthDate: null); + UserModel user = UserModel.create(name, null, null, null, null, null); + + await userRepository.createUser(user); emit(RedirectToClassSelection()); } Future checkUserAuthentication() async { try { var user = await userRepository.getUser(); - // Si la classe est vide - if (user.className == '') { + if (user.hasClassName == false || user.isEmpty) { emit(LoginInitial()); return false; } @@ -56,4 +59,8 @@ class LoginCubit extends Cubit { return false; } } + + Future logout() async { + emit(LoginInitial()); + } } diff --git a/frontend/app_student/lib/login/views/widgets/form/submit_button.dart b/frontend/app_student/lib/login/views/widgets/form/submit_button.dart index dbccac7..ea39e8e 100644 --- a/frontend/app_student/lib/login/views/widgets/form/submit_button.dart +++ b/frontend/app_student/lib/login/views/widgets/form/submit_button.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import '../../../../users/cubit/user_cubit.dart'; + class SubmitButton extends StatelessWidget { final TextEditingController nameController; @@ -17,9 +19,12 @@ class SubmitButton extends StatelessWidget { Widget build(BuildContext context) { return CustomButton( text: AppLocalizations.of(context)!.loginButton, - onPressed: () { + onPressed: () async { final String name = nameController.text.trim(); - context.read().saveLoginDetails(name); + await context.read().saveLoginDetails(name); + if (context.mounted) { + await context.read().fetchUser(); + } }, backgroundColor: CustomTheme.secondaryColor, textColor: Colors.white, diff --git a/frontend/app_student/lib/main.dart b/frontend/app_student/lib/main.dart index a951843..0332e2a 100644 --- a/frontend/app_student/lib/main.dart +++ b/frontend/app_student/lib/main.dart @@ -1,18 +1,22 @@ import 'package:app_student/routes.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:app_student/utils/custom_theme.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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 'api/api_service.dart'; +import 'api/users/repositories/user_repository.dart'; import 'config/config.dart'; import 'config/prod_config.dart'; import 'firebase_options.dart'; +import 'login/cubit/login_cubit.dart'; import 'utils/global.dart'; @pragma('vm:entry-point') @@ -35,9 +39,31 @@ void main() async { initializeDateFormatting('fr_FR', null).then((_) { runApp( - Provider( - create: (_) => ProdConfig(), - child: const MyApp(), + MultiRepositoryProvider( + providers: [ + RepositoryProvider( + create: (_) => ProdConfig(), + ), + RepositoryProvider( + create: (context) => UserRepository( + apiService: ApiService(apiUrl: context.read().apiUrl), + ), + ), + ], + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => + UserCubit(userRepository: context.read()) + ..fetchUser(), + ), + BlocProvider( + create: (context) => + LoginCubit(userRepository: context.read()), + ), + ], + child: const MyApp(), + ), ), ); }); diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart index b2ffe76..b99d7f7 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -1,25 +1,66 @@ import 'package:app_student/config/dev_config.dart'; import 'package:app_student/routes.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:app_student/utils/custom_theme.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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 'api/api_service.dart'; +import 'api/users/repositories/user_repository.dart'; import 'config/config.dart'; +import 'login/cubit/login_cubit.dart'; import 'utils/global.dart'; +@pragma('vm:entry-point') +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // If you're going to use other Firebase services in the background, such as Firestore, + // make sure you call `initializeApp` before using other Firebase services. + await Firebase.initializeApp(); +} + void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await Firebase.initializeApp(); + await dotenv.load(); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + initializeDateFormatting('fr_FR', null).then((_) { runApp( - Provider( - create: (_) => DevConfig(), - child: const MyApp(), + MultiRepositoryProvider( + providers: [ + RepositoryProvider( + create: (_) => DevConfig(), + ), + RepositoryProvider( + create: (context) => UserRepository( + apiService: ApiService(apiUrl: context.read().apiUrl), + ), + ), + ], + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => + UserCubit(userRepository: context.read()) + ..fetchUser(), + ), + BlocProvider( + create: (context) => + LoginCubit(userRepository: context.read()), + ), + ], + child: const MyApp(), + ), ), ); }); @@ -48,7 +89,15 @@ class MyApp extends StatelessWidget { return MaterialApp.router( debugShowCheckedModeBanner: false, title: 'my3iL', - theme: CustomTheme.theme, + theme: ThemeData( + primarySwatch: Colors.blue, + // Orange 3IL + focusColor: CustomTheme.secondaryColor, + primaryColor: CustomTheme.primaryColor, + // Bleu 3IL + secondaryHeaderColor: CustomTheme.primaryColor, + fontFamily: 'Arial', + ), localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, diff --git a/frontend/app_student/lib/menu/menu_view.dart b/frontend/app_student/lib/menu/menu_view.dart index cf1669a..64a7ebc 100644 --- a/frontend/app_student/lib/menu/menu_view.dart +++ b/frontend/app_student/lib/menu/menu_view.dart @@ -40,7 +40,7 @@ class MenuBarViewState extends State { switch (index) { case 0: - GoRouter.of(context).go(AppRoutes.accountPage); + GoRouter.of(context).go(AppRoutes.schoolSpace); break; case 1: GoRouter.of(context).go(AppRoutes.schedulePage); diff --git a/frontend/app_student/lib/profil/views/profil_page.dart b/frontend/app_student/lib/profil/views/profil_page.dart index bb6ef3d..4387428 100644 --- a/frontend/app_student/lib/profil/views/profil_page.dart +++ b/frontend/app_student/lib/profil/views/profil_page.dart @@ -1,3 +1,4 @@ +import 'package:app_student/login/cubit/login_cubit.dart'; import 'package:app_student/menu/menu_view.dart'; import 'package:app_student/profil/views/widgets/user_class_card.dart'; import 'package:app_student/routes.dart'; @@ -27,7 +28,7 @@ class ProfilPage extends StatelessWidget { builder: (context, state) { if (state is UserLoading) { return Container(); - } else if (state is UserNameLoaded) { + } else if (state is UserWihtoutLink) { final user = state.user; return Column( children: [ @@ -35,20 +36,22 @@ class ProfilPage extends StatelessWidget { padding: const EdgeInsets.only(left: 10.0), child: HeaderTitle( AppLocalizations.of(context)! - .profilMessageTitle(user.firstName), + .profilMessageTitle(user.firstName!), ), ), UserClassCard( className: user.className!, - firstName: user.firstName, - onTap: () { + firstName: user.firstName!, + onTap: () async { context.read().clearUserClass(); - GoRouter.of(context).go(AppRoutes.classListPage); + if (context.mounted) { + context.pushReplacement('/classList'); + } }, ), ], ); - } else if (state is UserLoaded) { + } else if (state is UserLoggedIn) { final user = state.user; final birthDateString = @@ -60,16 +63,15 @@ class ProfilPage extends StatelessWidget { padding: const EdgeInsets.only(left: 10.0), child: HeaderTitle( AppLocalizations.of(context)! - .profilMessageTitle(user.firstName), + .profilMessageTitle(user.firstName!), ), ), UserClassCard( className: user.className!, - firstName: user.firstName, - onTap: () { - context.read().clearUserClass(); - GoRouter.of(context).go(AppRoutes.classListPage); - }, + firstName: user.firstName!, onTap: () {}, + // onTap: () { + // context.read().clearUserClass(); + // }, ), UserInfoCard(ine: user.ine!, birthDate: birthDateString), ], @@ -87,9 +89,14 @@ class ProfilPage extends StatelessWidget { return CustomButton( text: AppLocalizations.of(context)!.disconnect, onPressed: () async { - final goRouter = GoRouter.of(context); - await context.read().deleteUser(); - goRouter.go(AppRoutes.loginPage); + await context.read().logout(); + if (context.mounted) { + await context.read().logout(); + } + if (context.mounted) { + context.pushReplacement(AppRoutes.loginPage); + } + WidgetsBinding.instance.addPostFrameCallback((_) { Fluttertoast.showToast( msg: AppLocalizations.of(context)!.disconnectedMessage, diff --git a/frontend/app_student/lib/routes.dart b/frontend/app_student/lib/routes.dart index 6972c21..4d426b5 100644 --- a/frontend/app_student/lib/routes.dart +++ b/frontend/app_student/lib/routes.dart @@ -6,6 +6,7 @@ import 'package:app_student/api/week_schedule/repositories/week_schedule_reposit import 'package:app_student/class_groups/cubit/class_group_cubit.dart'; import 'package:app_student/config/config.dart'; import 'package:app_student/profil/views/profil_page.dart'; +import 'package:app_student/school_space/views/forms/link_account_form.dart'; import 'package:app_student/school_space/views/school_space_page.dart'; import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:app_student/week_schedule/cubit/week_schedule_cubit.dart'; @@ -22,7 +23,8 @@ class AppRoutes { static const loginPage = '/login'; static const schedulePage = '/schedule'; static const profilPage = '/profil'; - static const accountPage = '/account'; + static const schoolSpace = '/school_space'; + static const linkAccountFormPage = '/linkAccountForm'; static final routes = [ GoRoute( @@ -35,10 +37,6 @@ class AppRoutes { create: (context) => ClassGroupRepository( apiService: ApiService(apiUrl: context.read().apiUrl))), - RepositoryProvider( - create: (context) => UserRepository( - apiService: - ApiService(apiUrl: context.read().apiUrl))), ], child: MultiBlocProvider(providers: [ BlocProvider( @@ -46,121 +44,97 @@ class AppRoutes { classRepository: context.read(), )..fetchClasses(), ), - BlocProvider( - create: (context) => - UserCubit(userRepository: context.read()) - ..fetchUser(), - ), ], child: const ClassGroupPage()), ), ), + // redirect: (context, state) { + // final loginCubit = context.read(); + // if (loginCubit.state is RedirectToClassSelection) { + // return classListPage; + // } + // return null; + // }, ), GoRoute( + redirect: (BuildContext context, GoRouterState state) { + final loginCubit = context.read(); + final userCubit = context.read(); + if (loginCubit.state is LoginAuthenticated) { + if (userCubit.state is UserWithoutClass) { + return classListPage; + } + return schedulePage; + } + return loginPage; + }, path: loginPage, pageBuilder: (context, state) => MaterialPage( key: state.pageKey, - child: MultiRepositoryProvider( + child: MultiBlocProvider( providers: [ - RepositoryProvider( - create: (context) => UserRepository( - apiService: - ApiService(apiUrl: context.read().apiUrl))), + BlocProvider( + create: (context) => + LoginCubit(userRepository: context.read()), + ), ], - child: MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => LoginCubit(context.read()), - ), - BlocProvider( - create: (context) => - UserCubit(userRepository: context.read()) - ..fetchUser(), - ), - ], - child: const LoginPage(), - ), + child: const LoginPage(), ), ), ), GoRoute( - path: schedulePage, - pageBuilder: (context, state) => MaterialPage( - key: state.pageKey, - child: MultiRepositoryProvider( - providers: [ - RepositoryProvider( - create: (context) => WeekScheduleRepository( - apiService: - ApiService(apiUrl: context.read().apiUrl))), - RepositoryProvider( - create: (context) => UserRepository( - apiService: - ApiService(apiUrl: context.read().apiUrl))), - ], - child: MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => WeekScheduleCubit( - 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(), - ), - ))), - GoRoute( - path: profilPage, + path: schedulePage, pageBuilder: (context, state) => MaterialPage( key: state.pageKey, child: MultiRepositoryProvider( providers: [ RepositoryProvider( - create: (context) => UserRepository( - apiService: - ApiService(apiUrl: context.read().apiUrl))), + create: (context) => WeekScheduleRepository( + apiService: ApiService(apiUrl: context.read().apiUrl), + ), + ), ], child: MultiBlocProvider( providers: [ BlocProvider( - create: (context) => - UserCubit(userRepository: context.read()) - ..fetchUser(), + create: (context) => WeekScheduleCubit( + weekScheduleRepository: + context.read(), + userCubit: context.read(), + initialDate: DateTime.now(), + )..fetchUserAndSchedule(), ), ], - child: const ProfilPage(), + child: const WeekSchedulePage(), ), ), ), + // redirect: (context, state) { + // final loginCubit = context.read(); + // if (loginCubit.state is LoginAuthenticated) { + // return schedulePage; + // } + // return null; + // }, ), GoRoute( - path: accountPage, + path: profilPage, pageBuilder: (context, state) => MaterialPage( key: state.pageKey, - child: MultiRepositoryProvider( - providers: [ - RepositoryProvider( - create: (context) => UserRepository( - apiService: - ApiService(apiUrl: context.read().apiUrl))), - ], - child: MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => - UserCubit(userRepository: context.read()), - ), - ], - child: const SchoolSpacePage(), - ), - ), + child: const ProfilPage(), + ), + ), + GoRoute( + path: schoolSpace, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: const SchoolSpacePage(), + ), + ), + GoRoute( + path: linkAccountFormPage, // Use the new constant here + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: const LinkAccountForm(), // Use the LinkAccountForm here ), ), ]; diff --git a/frontend/app_student/lib/school_space/views/forms/link_account_form.dart b/frontend/app_student/lib/school_space/views/forms/link_account_form.dart index 842762d..ef42111 100644 --- a/frontend/app_student/lib/school_space/views/forms/link_account_form.dart +++ b/frontend/app_student/lib/school_space/views/forms/link_account_form.dart @@ -5,19 +5,19 @@ import 'package:app_student/utils/custom_button.dart'; import 'package:app_student/utils/custom_layout.dart'; import 'package:app_student/utils/global.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; class LinkAccountForm extends StatelessWidget { - final UserCubit userCubit; - - const LinkAccountForm({super.key, required this.userCubit}); + const LinkAccountForm({super.key}); @override Widget build(BuildContext context) { final formKey = GlobalKey(); String ine = ''; String birthDate = ''; - final birthDateController = TextEditingController(); + final birthDateController = TextEditingController(text: birthDate); + final ineController = TextEditingController(text: ine); return CustomLayout( appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), @@ -29,6 +29,7 @@ class LinkAccountForm extends StatelessWidget { child: Column( children: [ TextFormField( + controller: ineController, decoration: const InputDecoration(labelText: 'INE'), validator: (value) { if (value == null || value.isEmpty) { @@ -41,6 +42,7 @@ class LinkAccountForm extends StatelessWidget { }, ), TextFormField( + controller: birthDateController, decoration: InputDecoration( labelText: 'Birth Date', suffixIcon: IconButton( @@ -70,7 +72,6 @@ class LinkAccountForm extends StatelessWidget { onSaved: (value) { birthDate = value!; }, - controller: birthDateController, ), ], ), @@ -82,13 +83,11 @@ class LinkAccountForm extends StatelessWidget { onPressed: () { if (formKey.currentState!.validate()) { formKey.currentState!.save(); - userCubit.loginAndSaveId(ine, birthDate); + context.read().loginAndSaveId(ine, birthDate); Navigator.of(context).pop(); } }, ), - // Add your bottomBar here if you have one - // bottomBar: YourBottomBar(), ); } } diff --git a/frontend/app_student/lib/school_space/views/pdf_view_page.dart b/frontend/app_student/lib/school_space/views/pdf_view_page.dart index d6c873b..a1331b2 100644 --- a/frontend/app_student/lib/school_space/views/pdf_view_page.dart +++ b/frontend/app_student/lib/school_space/views/pdf_view_page.dart @@ -1,27 +1,25 @@ -import 'package:app_student/utils/custom_layout.dart'; import 'package:flutter/material.dart'; import 'package:flutter_pdfview/flutter_pdfview.dart'; +import 'package:app_student/shared_components/app_bar.dart'; class PdfViewPage extends StatelessWidget { final String filePath; + final String documentTitle; - const PdfViewPage({super.key, required this.filePath}); + const PdfViewPage( + {super.key, required this.filePath, required this.documentTitle}); @override Widget build(BuildContext context) { - return CustomLayout( - appBar: AppBar( - title: const Text('PDF View'), - actions: [ - IconButton( - icon: const Icon(Icons.download), - onPressed: () { - // Implement your download functionality here - }, + return Scaffold( + appBar: const CustomAppBar(), + body: Column( + children: [ + Expanded( + child: PDFView(filePath: filePath), ), ], ), - body: PDFView(filePath: filePath), ); } } diff --git a/frontend/app_student/lib/school_space/views/school_space_page.dart b/frontend/app_student/lib/school_space/views/school_space_page.dart index bfb4da3..b462418 100644 --- a/frontend/app_student/lib/school_space/views/school_space_page.dart +++ b/frontend/app_student/lib/school_space/views/school_space_page.dart @@ -4,13 +4,12 @@ import 'package:app_student/utils/custom_theme.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 '../../menu/menu_view.dart'; import '../../shared_components/header_logo.dart'; import '../../shared_components/header_title.dart'; import '../../utils/custom_layout.dart'; import '../../utils/global.dart'; -import 'forms/link_account_form.dart'; import 'widgets/pdf_card.dart'; class SchoolSpacePage extends StatefulWidget { @@ -21,14 +20,6 @@ class SchoolSpacePage extends StatefulWidget { } class SchoolSpacePageState extends State { - late final UserCubit userCubit; - - @override - void initState() { - super.initState(); - userCubit = context.read(); - } - @override Widget build(BuildContext context) { return CustomLayout( @@ -36,7 +27,9 @@ class SchoolSpacePageState extends State { body: BlocBuilder( builder: (context, state) { if (state is UserLoading) { - return const Center(child: CircularProgressIndicator()); + return const Center( + child: CircularProgressIndicator(), + ); } else if (state is UserLoggedIn) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -49,7 +42,7 @@ class SchoolSpacePageState extends State { borderRadius: BorderRadius.circular(10.0), ), child: const Text( - 'Votre espace est relié ! Vous pouvez désormais consulter vos notes et absences :).', + 'Votre espace est relié ! Vous pouvez désormais consulter vos notes et absences :)', style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, @@ -57,16 +50,13 @@ class SchoolSpacePageState extends State { ), ), const SizedBox(height: 40), - PdfCard( - filePath: state.user.marksFile!.path, - title: 'Relevé de notes'), - const SizedBox(height: 20), - PdfCard( - filePath: state.user.absencesFile!.path, - title: "Relevé d'absences"), + ...?state.user.documents?.map((doc) => PdfCard( + filePath: doc.file!.path, + title: doc.title, + )), ], ); - } else if (state is UserInitial) { + } else if (state is UserWihtoutLink) { return Column( children: [ const Padding( @@ -91,13 +81,7 @@ class SchoolSpacePageState extends State { CustomButton( text: 'Relier mon compte', onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - LinkAccountForm(userCubit: userCubit), - ), - ); + context.push('/linkAccountForm'); }, ), ], diff --git a/frontend/app_student/lib/school_space/views/widgets/pdf_card.dart b/frontend/app_student/lib/school_space/views/widgets/pdf_card.dart index 957f2c8..3a12aa7 100644 --- a/frontend/app_student/lib/school_space/views/widgets/pdf_card.dart +++ b/frontend/app_student/lib/school_space/views/widgets/pdf_card.dart @@ -37,7 +37,10 @@ class PdfCard extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => PdfViewPage(filePath: filePath), + builder: (context) => PdfViewPage( + filePath: filePath, + documentTitle: 'Test', + ), ), ); }, diff --git a/frontend/app_student/lib/shared_components/app_bar.dart b/frontend/app_student/lib/shared_components/app_bar.dart index 908c5e3..204d6eb 100644 --- a/frontend/app_student/lib/shared_components/app_bar.dart +++ b/frontend/app_student/lib/shared_components/app_bar.dart @@ -15,9 +15,9 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { final userState = context.watch().state; String className = ''; - if (userState is UserLoaded) { + if (userState is UserLoggedIn) { className = (userState).user.className ?? ''; - } else if (userState is UserNameLoaded) { + } else if (userState is UserWihtoutLink) { className = (userState).user.className ?? ''; } return AppBar( diff --git a/frontend/app_student/lib/users/cubit/user_cubit.dart b/frontend/app_student/lib/users/cubit/user_cubit.dart index 2c686f7..82704e8 100644 --- a/frontend/app_student/lib/users/cubit/user_cubit.dart +++ b/frontend/app_student/lib/users/cubit/user_cubit.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - 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'; @@ -21,21 +19,24 @@ class UserCubit extends Cubit { emit(UserLoading()); final user = await userRepository.getUser(); - if (user.className == null || user.className!.isEmpty) { - emit(UserWithoutClass(user)); - } else if (user.ine != null && - user.ine!.isNotEmpty && - user.birthDate != null && - user.studentId != null) { - emit(UserLoaded(user)); - } else if (user.ine == null || - (user.ine != null && user.ine!.isEmpty) || - user.birthDate == null) { - emit(UserNameLoaded(user)); + if (user.isEmpty) { + emit(UserInitial()); + } else if (user.hasFirstName) { + if (user.hasClassName == false) { + emit(UserWithoutClass(user)); + } else if (user.hasIne && user.hasBirthDate && user.hasStudentId) { + emit(UserLoggedIn(user)); + } else { + emit(UserWihtoutLink(user)); + } } } catch (e) { if (!isClosed) { - emit(UserError(e.toString())); + if (e.toString() == 'Exception: No user found in cache') { + emit(UserInitial()); + } else { + emit(UserError(e.toString())); + } } } } @@ -45,14 +46,17 @@ class UserCubit extends Cubit { } Future saveUserClass(ClassGroupModel classGroup) async { - await userRepository.saveUserClass(classGroup.name.toString()); + UserModel user = await userRepository.getUser(); + user.entity.className = classGroup.name; await FirebaseMessaging.instance .subscribeToTopic(classGroup.name.toString().replaceAll(' ', '')); - emit(UserClassesSelected()); + + await userRepository.createUser(user); + + emit(UserWihtoutLink(user)); } Future deleteUser() async { - // On se désabonne de tous les topics final user = await userRepository.getUser(); if (user.className != null) { await FirebaseMessaging.instance @@ -61,53 +65,25 @@ class UserCubit extends Cubit { await userRepository.delete(); } + Future logout() async { + await deleteUser(); + emit(UserInitial()); + } + Future clearUserClass() async { await userRepository.clearClass(); + emit(UserWithoutClass(await userRepository.getUser())); } Future loginAndSaveId(String username, String password) async { emit(UserLoading()); try { - final studentId = await userRepository.login(username, password); - Global.setIne(username); - Global.setBirthDate(password); - Global.setStudentId(studentId); - final user = await userRepository.getUser(); - emit(UserLoggedIn(user)); - } catch (e) { - emit(UserError(e.toString())); - } - } + final user = await userRepository.login(username, password); + await Global.setUser(user); - Future fetchMarks() async { - emit(UserLoading()); - try { - final user = await userRepository.getUser(); - if (user.studentId != null) { - final File marks = await userRepository.getMarks(user.studentId!); - if (kDebugMode) { - print(marks.path); - } - final File absences = await userRepository.getAbsences(user.studentId!); - if (kDebugMode) { - print(absences.path); - } - emit(UserLoaded(user)); - } else { - throw Exception('No student ID found in SharedPreferences'); - } + emit(UserLoggedIn(user)); } catch (e) { emit(UserError(e.toString())); } } - - Future checkStudentId() async { - final user = await getCurrentUser(); - - if (user.studentId != null) { - emit(UserLoggedIn(user)); - } else { - emit(UserInitial()); - } - } } diff --git a/frontend/app_student/lib/users/cubit/user_state.dart b/frontend/app_student/lib/users/cubit/user_state.dart index 905c622..066cf33 100644 --- a/frontend/app_student/lib/users/cubit/user_state.dart +++ b/frontend/app_student/lib/users/cubit/user_state.dart @@ -38,3 +38,11 @@ class UserLoggedIn extends UserState { UserLoggedIn(this.user); } + +class UserWihtoutLink extends UserState { + final UserModel user; + + UserWihtoutLink(this.user); +} + +class UserLoggedOut extends UserState {} diff --git a/frontend/app_student/lib/utils/global.dart b/frontend/app_student/lib/utils/global.dart index 5661e67..4492b07 100644 --- a/frontend/app_student/lib/utils/global.dart +++ b/frontend/app_student/lib/utils/global.dart @@ -1,5 +1,7 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:app_student/api/users/models/user_model.dart'; class Global { static late double screenWidth; @@ -14,33 +16,19 @@ class Global { return await SharedPreferences.getInstance(); } - static Future get studentId async { + static Future setUser(UserModel user) async { final prefs = await SharedPreferences.getInstance(); - return prefs.getInt('studentId'); + String userJson = jsonEncode(user.toJson()); + prefs.setString('user', userJson); } - static Future setStudentId(int value) async { + static Future getUser() async { final prefs = await SharedPreferences.getInstance(); - prefs.setInt('studentId', value); - } - - static Future get ine async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('ine') ?? ''; - } - - static Future setIne(String value) async { - final prefs = await SharedPreferences.getInstance(); - prefs.setString('ine', value); - } - - static Future get birthDate async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('birthDate') ?? ''; - } - - static Future setBirthDate(String value) async { - final prefs = await SharedPreferences.getInstance(); - prefs.setString('birthDate', value); + String? userJson = prefs.getString('user'); + if (userJson != null) { + return UserModel.fromJson(jsonDecode(userJson)); + } else { + return null; + } } } diff --git a/frontend/app_student/lib/week_schedule/views/week_schedule_page.dart b/frontend/app_student/lib/week_schedule/views/week_schedule_page.dart index 324e372..c732f3e 100644 --- a/frontend/app_student/lib/week_schedule/views/week_schedule_page.dart +++ b/frontend/app_student/lib/week_schedule/views/week_schedule_page.dart @@ -1,13 +1,9 @@ -import 'package:app_student/api/week_schedule/repositories/week_schedule_repository.dart'; import 'package:app_student/shared_components/network_error.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/datepicker_button.dart'; import 'package:app_student/week_schedule/views/widgets/day_schedule_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../api/users/repositories/user_repository.dart'; import '../../menu/menu_view.dart'; import '../../shared_components/app_bar.dart'; import '../../utils/custom_layout.dart'; @@ -19,63 +15,46 @@ class WeekSchedulePage extends StatelessWidget { @override Widget build(BuildContext context) { - final weekScheduleRepository = - RepositoryProvider.of(context); - final userRepository = RepositoryProvider.of(context); - final weekScheduleCubit = WeekScheduleCubit( - userCubit: context.read(), - weekScheduleRepository: weekScheduleRepository, - initialDate: initialDate); - - return BlocProvider( - create: (context) => weekScheduleCubit..fetchUserAndSchedule(), - child: BlocProvider( - create: (context) => - UserCubit(userRepository: userRepository)..fetchUser(), - child: CustomLayout( - appBar: const CustomAppBar(widget: DatePickerButton()), - body: BlocBuilder( - builder: (context, state) { - final pageController = PageController( - initialPage: - state is WeekScheduleLoaded && state.todayIndex != -1 - ? state.todayIndex - : 0, - ); - if (state is WeekScheduleLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is WeekScheduleLoaded) { - final allEvents = state.weekSchedule - .expand((week) => week.daySchedules) - .toList(); + return CustomLayout( + appBar: const CustomAppBar(widget: DatePickerButton()), + body: BlocBuilder( + builder: (context, state) { + final pageController = PageController( + initialPage: state is WeekScheduleLoaded && state.todayIndex != -1 + ? state.todayIndex + : 0, + ); + 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: 20.0), - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: PageView.builder( - controller: pageController, - itemCount: allEvents.length, - itemBuilder: (context, index) { - final daySchedule = allEvents[index]; - return DayScheduleWidget( - daySchedule: daySchedule, - pageController: pageController, - ); - }, - ), - ), - ); - } else if (state is WeekScheduleError) { - return const NetworkError(); - } else { - return const SizedBox.shrink(); - } - }, - ), - bottomBar: const MenuBarView(), - ), + return Padding( + padding: const EdgeInsets.only(top: 20.0), + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: PageView.builder( + controller: pageController, + itemCount: allEvents.length, + itemBuilder: (context, index) { + final daySchedule = allEvents[index]; + return DayScheduleWidget( + daySchedule: daySchedule, + pageController: pageController, + ); + }, + ), + ), + ); + } else if (state is WeekScheduleError) { + return const NetworkError(); + } else { + return const SizedBox.shrink(); + } + }, ), + bottomBar: const MenuBarView(), ); } }