From 4bf816d1a5e01284534865a17008db73e08e156b Mon Sep 17 00:00:00 2001 From: Jules Artaud Date: Sun, 21 Apr 2024 16:05:38 +0200 Subject: [PATCH 1/7] :construction:: fetch pdf data --- .../lib/account/account_cubit.dart | 54 ++++++++++ .../lib/account/account_state.dart | 23 ++++ .../lib/account/views/account.dart | 102 ++++++++++++++++++ .../account/views/widgets/account_dialog.dart | 63 +++++++++++ .../repositories/account_repository.dart | 64 +++++++++++ frontend/app_student/lib/main_dev.dart | 2 +- frontend/app_student/lib/routes.dart | 27 +++++ frontend/app_student/lib/utils/global.dart | 17 ++- .../Flutter/GeneratedPluginRegistrant.swift | 2 + frontend/app_student/pubspec.lock | 32 ++++++ frontend/app_student/pubspec.yaml | 2 + 11 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 frontend/app_student/lib/account/account_cubit.dart create mode 100644 frontend/app_student/lib/account/account_state.dart create mode 100644 frontend/app_student/lib/account/views/account.dart create mode 100644 frontend/app_student/lib/account/views/widgets/account_dialog.dart create mode 100644 frontend/app_student/lib/api/account/repositories/account_repository.dart diff --git a/frontend/app_student/lib/account/account_cubit.dart b/frontend/app_student/lib/account/account_cubit.dart new file mode 100644 index 0000000..5e5ae58 --- /dev/null +++ b/frontend/app_student/lib/account/account_cubit.dart @@ -0,0 +1,54 @@ + +import 'dart:io'; + +import 'package:app_student/utils/global.dart'; +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; +import 'package:app_student/api/account/repositories/account_repository.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +part 'account_state.dart'; + +class AccountCubit extends Cubit { + final AccountRepository accountRepository; + + AccountCubit({required this.accountRepository}) : super(AccountInitial()); + + Future loginAndSaveId(String username, String password) async { + emit(AccountLoading()); + try { + final studentId = await accountRepository.login(username, password); + emit(AccountLoggedIn(studentId: studentId)); + } catch (e) { + emit(AccountError()); + } + } + + Future fetchMarks() async { + emit(AccountLoading()); + try { + final studentId = await Global.studentId; + if (studentId != null) { + final File marks = await accountRepository.getMarks(studentId); + final File absences = await accountRepository.getAbsences(studentId); + emit(AccountLoaded(marksFile: marks, absencesFile: absences)); + } else { + throw Exception('No student ID found in SharedPreferences'); + } + } catch (e) { + emit(AccountError()); + } + } + + Future checkStudentId() async { + int? id = await Global.studentId; + + if (id != null) { + emit(AccountLoggedIn(studentId: id)); + } else { + emit(AccountInitial()); + } + } + + +} diff --git a/frontend/app_student/lib/account/account_state.dart b/frontend/app_student/lib/account/account_state.dart new file mode 100644 index 0000000..dda9e8f --- /dev/null +++ b/frontend/app_student/lib/account/account_state.dart @@ -0,0 +1,23 @@ +part of 'account_cubit.dart'; + +@immutable +abstract class AccountState {} + +class AccountInitial extends AccountState {} + +class AccountLoading extends AccountState {} + +class AccountError extends AccountState {} + +class AccountLoggedIn extends AccountState { + final int studentId; + + AccountLoggedIn({required this.studentId}); +} + +class AccountLoaded extends AccountState { + final File marksFile; + final File absencesFile; + + AccountLoaded({required this.absencesFile, required this.marksFile}); +} diff --git a/frontend/app_student/lib/account/views/account.dart b/frontend/app_student/lib/account/views/account.dart new file mode 100644 index 0000000..c62cdd6 --- /dev/null +++ b/frontend/app_student/lib/account/views/account.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_pdfview/flutter_pdfview.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 '../account_cubit.dart'; +import 'widgets/account_dialog.dart'; + +class AccountPage extends StatefulWidget { + const AccountPage({super.key}); + + @override + _AccountPageState createState() => _AccountPageState(); +} + +class _AccountPageState extends State { + late final AccountCubit accountCubit; + + @override + void initState() { + super.initState(); + accountCubit = BlocProvider.of(context); + accountCubit.checkStudentId(); + + accountCubit.stream.listen((state) { + if (state is AccountLoggedIn) { + accountCubit.fetchMarks(); + } + }); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + print("caca"); + print(state); + if (state is AccountLoading) { + return const CircularProgressIndicator(); + } + + if (state is AccountLoaded) { + return CustomLayout( + appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), + body: Column( + children: [ + const Padding( + padding: EdgeInsets.only(left: 10.0), + child: HeaderTitle('coucou'), + ), + Expanded( + child: PDFView( + filePath: state.marksFile.path, + ), + ), + Expanded( + child: PDFView( + filePath: state.absencesFile.path, + ), + ), + ], + ), + bottomBar: const MenuBarView(), + ); + } + + if (state is AccountInitial) { + return CustomLayout( + appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), + body: Column( + children: [ + const Padding( + padding: EdgeInsets.only(left: 10.0), + child: HeaderTitle('coucou'), + ), + Center( + child: ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => + AccountDialog(accountCubit: accountCubit), + ); + }, + child: const Text('Relier mon compte'), + ), + ), + ], + ), + bottomBar: const MenuBarView(), + ); + } + + return const SizedBox.shrink(); + }, + ); + } +} \ No newline at end of file diff --git a/frontend/app_student/lib/account/views/widgets/account_dialog.dart b/frontend/app_student/lib/account/views/widgets/account_dialog.dart new file mode 100644 index 0000000..ce7b1fe --- /dev/null +++ b/frontend/app_student/lib/account/views/widgets/account_dialog.dart @@ -0,0 +1,63 @@ +import 'package:app_student/account/account_cubit.dart'; +import 'package:flutter/material.dart'; + +class AccountDialog extends StatelessWidget { + final AccountCubit accountCubit; + + const AccountDialog({super.key, required this.accountCubit}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + String ine = ''; + String birthDate = ''; + + return AlertDialog( + title: const Text('Login'), + content: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + decoration: const InputDecoration(labelText: 'INE'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your INE'; + } + return null; + }, + onSaved: (value) { + ine = value!; + }, + ), + TextFormField( + decoration: const InputDecoration(labelText: 'Birth Date'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your birth date'; + } + return null; + }, + onSaved: (value) { + birthDate = value!; + }, + ), + ], + ), + ), + actions: [ + TextButton( + child: const Text('Submit'), + onPressed: () { + if (formKey.currentState!.validate()) { + formKey.currentState!.save(); + accountCubit.loginAndSaveId(ine, birthDate); + Navigator.of(context).pop(); + } + }, + ), + ], + ); + } +} \ No newline at end of file diff --git a/frontend/app_student/lib/api/account/repositories/account_repository.dart b/frontend/app_student/lib/api/account/repositories/account_repository.dart new file mode 100644 index 0000000..0fc90e4 --- /dev/null +++ b/frontend/app_student/lib/api/account/repositories/account_repository.dart @@ -0,0 +1,64 @@ +import 'dart:io'; +import 'package:app_student/api/api_service.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; + +import '../../../utils/global.dart'; + +class AccountRepository { + final ApiService apiService; + + AccountRepository({required this.apiService}); + + Future 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/main_dev.dart b/frontend/app_student/lib/main_dev.dart index 1c65c51..4d5c10d 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -32,7 +32,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { final router = GoRouter( routes: AppRoutes.routes, - initialLocation: '/login', + initialLocation: '/account', errorPageBuilder: (context, state) { return MaterialPage( child: Scaffold( diff --git a/frontend/app_student/lib/routes.dart b/frontend/app_student/lib/routes.dart index 7daa713..59e9a75 100644 --- a/frontend/app_student/lib/routes.dart +++ b/frontend/app_student/lib/routes.dart @@ -1,4 +1,5 @@ // lib/routes.dart +import 'package:app_student/account/views/account.dart'; import 'package:app_student/api/api_service.dart'; import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; import 'package:app_student/api/users/repositories/user_repository.dart'; @@ -13,6 +14,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'account/account_cubit.dart'; +import 'api/account/repositories/account_repository.dart'; import 'class_groups/views/class_group.dart'; import 'login/cubit/login_cubit.dart'; import 'login/views/login.dart'; @@ -22,6 +25,7 @@ class AppRoutes { static const loginPage = '/login'; static const schedulePage = '/schedule'; static const profilPage = '/profil'; + static const accountPage = '/account'; static final routes = [ GoRoute( @@ -127,5 +131,28 @@ class AppRoutes { ), ), ), + GoRoute( + path: accountPage, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: MultiRepositoryProvider( + providers: [ + RepositoryProvider( + create: (context) => AccountRepository( + apiService: + ApiService(apiUrl: context.read().apiUrl))), + ], + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => AccountCubit( + accountRepository: context.read()), + ), + ], + child: const AccountPage(), + ), + ), + ), + ), ]; } diff --git a/frontend/app_student/lib/utils/global.dart b/frontend/app_student/lib/utils/global.dart index ea271ab..8965907 100644 --- a/frontend/app_student/lib/utils/global.dart +++ b/frontend/app_student/lib/utils/global.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class Global { static late double screenWidth; @@ -8,4 +9,18 @@ class Global { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; } -} + + static Future get prefs async { + return await SharedPreferences.getInstance(); + } + + static Future get studentId async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getInt('studentId'); + } + + static Future setStudentId(int value) async { + final prefs = await SharedPreferences.getInstance(); + prefs.setInt('studentId', value); + } +} \ No newline at end of file diff --git a/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift index 724bb2a..b8e2b22 100644 --- a/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import path_provider_foundation import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/frontend/app_student/pubspec.lock b/frontend/app_student/pubspec.lock index 939c271..69c35ad 100644 --- a/frontend/app_student/pubspec.lock +++ b/frontend/app_student/pubspec.lock @@ -123,6 +123,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_pdfview: + dependency: "direct main" + description: + name: flutter_pdfview + sha256: a9055bf920c7095bf08c2781db431ba23577aa5da5a056a7152dc89a18fbec6f + url: "https://pub.dev" + source: hosted + version: "1.3.2" flutter_svg: dependency: "direct main" description: @@ -269,6 +277,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + url: "https://pub.dev" + source: hosted + version: "2.2.4" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://pub.dev" + source: hosted + version: "2.3.2" path_provider_linux: dependency: transitive description: diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index 1508b86..4293507 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -48,6 +48,8 @@ dependencies: flutter_localizations: sdk: flutter fluttertoast: ^8.2.4 + path_provider: ^2.1.3 + flutter_pdfview: ^1.3.2 dev_dependencies: From d5c7b264bc8afe4d5f06ac8289955aedc06ea331 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Apr 2024 20:11:31 +0200 Subject: [PATCH 2/7] :construction: : ui/ux marks and absences --- .../lib/account/views/account.dart | 112 ++++++++++-------- .../views/forms/account_link_form.dart | 68 +++++++++++ .../lib/account/views/widgets/pdf_card.dart | 46 +++++++ .../lib/account/views/widgets/pdf_view.dart | 27 +++++ .../users/repositories/user_repository.dart | 1 + frontend/app_student/lib/l10n/app_fr.arb | 4 + frontend/app_student/lib/main_dev.dart | 3 + frontend/app_student/lib/menu/menu_view.dart | 15 +-- .../app_student/lib/profils/views/profil.dart | 19 ++- .../lib/shared_components/header_logo.dart | 9 +- 10 files changed, 242 insertions(+), 62 deletions(-) create mode 100644 frontend/app_student/lib/account/views/forms/account_link_form.dart create mode 100644 frontend/app_student/lib/account/views/widgets/pdf_card.dart create mode 100644 frontend/app_student/lib/account/views/widgets/pdf_view.dart diff --git a/frontend/app_student/lib/account/views/account.dart b/frontend/app_student/lib/account/views/account.dart index c62cdd6..7547831 100644 --- a/frontend/app_student/lib/account/views/account.dart +++ b/frontend/app_student/lib/account/views/account.dart @@ -1,6 +1,9 @@ +import 'package:app_student/account/views/forms/account_link_form.dart'; +import 'package:app_student/account/views/widgets/pdf_card.dart'; +import 'package:app_student/utils/custom_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_pdfview/flutter_pdfview.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../menu/menu_view.dart'; import '../../shared_components/header_logo.dart'; @@ -8,7 +11,6 @@ import '../../shared_components/header_title.dart'; import '../../utils/custom_layout.dart'; import '../../utils/global.dart'; import '../account_cubit.dart'; -import 'widgets/account_dialog.dart'; class AccountPage extends StatefulWidget { const AccountPage({super.key}); @@ -35,68 +37,84 @@ class _AccountPageState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - print("caca"); - print(state); - if (state is AccountLoading) { - return const CircularProgressIndicator(); - } - - if (state is AccountLoaded) { - return CustomLayout( - appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), - body: Column( + return CustomLayout( + appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), + body: BlocBuilder( + builder: (context, state) { + if (state is AccountLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is AccountLoaded) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Padding( - padding: EdgeInsets.only(left: 10.0), - child: HeaderTitle('coucou'), - ), - Expanded( - child: PDFView( - filePath: state.marksFile.path, + HeaderTitle(AppLocalizations.of(context)!.my3il), + Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: CustomTheme.primaryColorLight.withOpacity(0.1), + borderRadius: BorderRadius.circular(10.0), ), - ), - Expanded( - child: PDFView( - filePath: state.absencesFile.path, + child: const Text( + 'Votre espace est relié ! Vous pouvez désormais consulter vos notes et absences :).', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), ), ), + const SizedBox(height: 40), + PdfCard( + filePath: state.marksFile.path, title: 'Relevé de notes'), + const SizedBox(height: 20), + PdfCard( + filePath: state.absencesFile.path, + title: "Relevé d'absences"), ], - ), - bottomBar: const MenuBarView(), - ); - } - - if (state is AccountInitial) { - return CustomLayout( - appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), - body: Column( + ); + } else if (state is AccountInitial) { + return Column( children: [ const Padding( padding: EdgeInsets.only(left: 10.0), - child: HeaderTitle('coucou'), + child: HeaderTitle('My3iL'), + ), + Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: CustomTheme.primaryColorLight.withOpacity(0.1), + borderRadius: BorderRadius.circular(10.0), + ), + child: const Text( + 'Cet espace vous permet de relier votre compte My3iL à l\'application pour consulter vos notes et absences.', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), ), + const SizedBox(height: 20), Center( child: ElevatedButton( onPressed: () { - showDialog( - context: context, - builder: (context) => - AccountDialog(accountCubit: accountCubit), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + LoginFormPage(accountCubit: accountCubit), + ), ); }, child: const Text('Relier mon compte'), ), ), ], - ), - bottomBar: const MenuBarView(), - ); - } - - return const SizedBox.shrink(); - }, + ); + } else { + return const SizedBox.shrink(); + } + }, + ), + bottomBar: const MenuBarView(), ); } -} \ No newline at end of file +} diff --git a/frontend/app_student/lib/account/views/forms/account_link_form.dart b/frontend/app_student/lib/account/views/forms/account_link_form.dart new file mode 100644 index 0000000..4642949 --- /dev/null +++ b/frontend/app_student/lib/account/views/forms/account_link_form.dart @@ -0,0 +1,68 @@ +import 'package:app_student/account/account_cubit.dart'; +import 'package:app_student/shared_components/header_logo.dart'; +import 'package:app_student/utils/custom_layout.dart'; +import 'package:app_student/utils/global.dart'; +import 'package:flutter/material.dart'; + +class LoginFormPage extends StatelessWidget { + final AccountCubit accountCubit; + + const LoginFormPage({super.key, required this.accountCubit}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + String ine = ''; + String birthDate = ''; + + return CustomLayout( + appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: formKey, + child: Column( + children: [ + TextFormField( + decoration: const InputDecoration(labelText: 'INE'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your INE'; + } + return null; + }, + onSaved: (value) { + ine = value!; + }, + ), + TextFormField( + decoration: const InputDecoration(labelText: 'Birth Date'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your birth date'; + } + return null; + }, + onSaved: (value) { + birthDate = value!; + }, + ), + ElevatedButton( + child: const Text('Submit'), + onPressed: () { + if (formKey.currentState!.validate()) { + formKey.currentState!.save(); + accountCubit.loginAndSaveId(ine, birthDate); + Navigator.of(context).pop(); + } + }, + ), + ], + ), + ), + ), + // Add your bottomBar here if you have one + // bottomBar: YourBottomBar(), + ); + } +} diff --git a/frontend/app_student/lib/account/views/widgets/pdf_card.dart b/frontend/app_student/lib/account/views/widgets/pdf_card.dart new file mode 100644 index 0000000..cf36400 --- /dev/null +++ b/frontend/app_student/lib/account/views/widgets/pdf_card.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:app_student/account/views/widgets/pdf_view.dart'; +import 'package:app_student/utils/custom_theme.dart'; + +class PdfCard extends StatelessWidget { + final String filePath; + final String title; + + const PdfCard({required this.filePath, required this.title}); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: CustomTheme.primaryColor, + borderRadius: BorderRadius.circular(10.0), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + spreadRadius: 1, + blurRadius: 2, + offset: Offset(0, 1), + ), + ], + ), + child: ListTile( + contentPadding: const EdgeInsets.all(10.0), + leading: const Icon(Icons.picture_as_pdf, color: Colors.white), + title: Text( + title, + style: const TextStyle(color: Colors.white), + ), + trailing: const Icon(Icons.arrow_forward_ios, color: Colors.white), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PdfViewPage(filePath: filePath), + ), + ); + }, + ), + ); + } +} \ No newline at end of file diff --git a/frontend/app_student/lib/account/views/widgets/pdf_view.dart b/frontend/app_student/lib/account/views/widgets/pdf_view.dart new file mode 100644 index 0000000..caa3bac --- /dev/null +++ b/frontend/app_student/lib/account/views/widgets/pdf_view.dart @@ -0,0 +1,27 @@ +import 'package:app_student/utils/custom_layout.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_pdfview/flutter_pdfview.dart'; + +class PdfViewPage extends StatelessWidget { + final String filePath; + + const PdfViewPage({super.key, required this.filePath}); + + @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 + }, + ), + ], + ), + body: PDFView(filePath: filePath), + ); + } +} \ No newline at end of file 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 ff6096c..7932bdb 100644 --- a/frontend/app_student/lib/api/users/repositories/user_repository.dart +++ b/frontend/app_student/lib/api/users/repositories/user_repository.dart @@ -61,6 +61,7 @@ class UserRepository { await prefs.remove('name'); await prefs.remove('birthDate'); await prefs.remove('className'); + await prefs.remove('studentId'); } Future clearClass() async { diff --git a/frontend/app_student/lib/l10n/app_fr.arb b/frontend/app_student/lib/l10n/app_fr.arb index e70de85..31f4c0a 100644 --- a/frontend/app_student/lib/l10n/app_fr.arb +++ b/frontend/app_student/lib/l10n/app_fr.arb @@ -129,5 +129,9 @@ "confirm": "Confirmer", "@confirm": { "description": "Texte du bouton pour confirmer" + }, + "my3il": "Mon 3il", + "@my3il": { + "description": "Texte du menu" } } \ 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 4d5c10d..abdf9f0 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -50,6 +50,9 @@ class MyApp extends StatelessWidget { title: '3iL Student App', theme: ThemeData( primarySwatch: Colors.blue, + appBarTheme: const AppBarTheme( + iconTheme: IconThemeData(color: Colors.white), + ), // Orange 3IL focusColor: CustomTheme.secondaryColor, primaryColor: CustomTheme.primaryColor, diff --git a/frontend/app_student/lib/menu/menu_view.dart b/frontend/app_student/lib/menu/menu_view.dart index 9a7b4ef..7a3f6f9 100644 --- a/frontend/app_student/lib/menu/menu_view.dart +++ b/frontend/app_student/lib/menu/menu_view.dart @@ -44,16 +44,7 @@ class MenuBarViewState extends State { switch (index) { case 0: - context.read().deleteUser(); - GoRouter.of(context).go(AppRoutes.loginPage); - WidgetsBinding.instance.addPostFrameCallback((_) { - Fluttertoast.showToast( - msg: AppLocalizations.of(context)!.disconnectedMessage, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - textColor: Colors.white, - ); - }); + GoRouter.of(context).go(AppRoutes.accountPage); break; case 1: GoRouter.of(context).go(AppRoutes.schedulePage); @@ -69,10 +60,10 @@ class MenuBarViewState extends State { return BottomNavigationBar( items: [ MenuIcon( - iconPath: 'assets/images/disconnect.svg', + iconPath: 'assets/images/school.svg', selectedIndex: _selectedIndex, itemIndex: 0, - label: AppLocalizations.of(context)!.disconnect, + label: AppLocalizations.of(context)!.my3il, ), MenuIcon( iconPath: 'assets/images/calendar.svg', diff --git a/frontend/app_student/lib/profils/views/profil.dart b/frontend/app_student/lib/profils/views/profil.dart index 573b3db..29aba02 100644 --- a/frontend/app_student/lib/profils/views/profil.dart +++ b/frontend/app_student/lib/profils/views/profil.dart @@ -1,10 +1,13 @@ import 'package:app_student/menu/menu_view.dart'; import 'package:app_student/profils/views/widgets/class_group_button.dart'; import 'package:app_student/profils/views/widgets/user_class_card.dart'; +import 'package:app_student/routes.dart'; import 'package:app_student/utils/custom_layout.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:go_router/go_router.dart'; import '../../shared_components/header_logo.dart'; import '../../shared_components/header_title.dart'; @@ -45,9 +48,23 @@ class ProfilPage extends StatelessWidget { ), UserClassCard(className: className, firstName: firstName), // UserInfoCard(ine: ine, birthDate: birthDateString), + const ClassGroupButton(), ], ), - bottomContent: const ClassGroupButton(), + bottomContent: ElevatedButton( + onPressed: () { + context.read().deleteUser(); + GoRouter.of(context).go(AppRoutes.loginPage); + WidgetsBinding.instance.addPostFrameCallback((_) { + Fluttertoast.showToast( + msg: AppLocalizations.of(context)!.disconnectedMessage, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + textColor: Colors.white, + ); + }); + }, + child: Text(AppLocalizations.of(context)!.disconnect)), bottomBar: const MenuBarView(), ); } diff --git a/frontend/app_student/lib/shared_components/header_logo.dart b/frontend/app_student/lib/shared_components/header_logo.dart index 4ea8662..dacf29c 100644 --- a/frontend/app_student/lib/shared_components/header_logo.dart +++ b/frontend/app_student/lib/shared_components/header_logo.dart @@ -3,13 +3,18 @@ import 'package:flutter/material.dart'; class HeaderLogo extends StatelessWidget implements PreferredSizeWidget { final double appBarHeight; + final bool showBackButton; - const HeaderLogo({super.key, required this.appBarHeight}); + const HeaderLogo({super.key, required this.appBarHeight, this.showBackButton = false}); @override Widget build(BuildContext context) { return AppBar( backgroundColor: CustomTheme.primaryColor, + leading: showBackButton ? IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.of(context).pop(), + ) : null, flexibleSpace: Center( child: SizedBox( width: 200.0, @@ -24,4 +29,4 @@ class HeaderLogo extends StatelessWidget implements PreferredSizeWidget { Size get preferredSize { return Size.fromHeight(appBarHeight); } -} +} \ No newline at end of file From 1ab645467760ab90fd50338bf0c779328807f38f Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 24 Apr 2024 23:14:06 +0200 Subject: [PATCH 3/7] :necktie: : updated profil logic cubit --- .../lib/account/views/account.dart | 2 +- .../lib/api/users/entities/user_entity.dart | 5 +- .../lib/api/users/models/user_model.dart | 5 +- .../users/repositories/user_repository.dart | 48 ++++---- .../lib/class_groups/views/class_group.dart | 5 +- .../class_groups/views/widgets/card_list.dart | 1 + .../lib/login/cubit/login_cubit.dart | 12 +- .../lib/login/views/login_view.dart | 2 +- .../views/widgets/form/button_submit.dart | 17 +-- .../login/views/widgets/form/form_login.dart | 52 ++------- frontend/app_student/lib/main_dev.dart | 15 +-- frontend/app_student/lib/menu/menu_view.dart | 2 - .../app_student/lib/profil/views/profil.dart | 110 ++++++++++++++++++ .../views/widgets/class_group_button.dart | 0 .../views/widgets/user_class_card.dart | 0 .../views/widgets/user_info_card.dart | 0 .../app_student/lib/profils/views/profil.dart | 71 ----------- frontend/app_student/lib/routes.dart | 2 +- .../lib/users/cubit/user_cubit.dart | 6 +- .../lib/users/cubit/user_state.dart | 8 ++ .../app_student/lib/utils/custom_layout.dart | 21 ++-- .../app_student/lib/utils/custom_theme.dart | 12 ++ 22 files changed, 206 insertions(+), 190 deletions(-) create mode 100644 frontend/app_student/lib/profil/views/profil.dart rename frontend/app_student/lib/{profils => profil}/views/widgets/class_group_button.dart (100%) rename frontend/app_student/lib/{profils => profil}/views/widgets/user_class_card.dart (100%) rename frontend/app_student/lib/{profils => profil}/views/widgets/user_info_card.dart (100%) delete mode 100644 frontend/app_student/lib/profils/views/profil.dart diff --git a/frontend/app_student/lib/account/views/account.dart b/frontend/app_student/lib/account/views/account.dart index 7547831..3c7b41e 100644 --- a/frontend/app_student/lib/account/views/account.dart +++ b/frontend/app_student/lib/account/views/account.dart @@ -85,7 +85,7 @@ class _AccountPageState extends State { borderRadius: BorderRadius.circular(10.0), ), child: const Text( - 'Cet espace vous permet de relier votre compte My3iL à l\'application pour consulter vos notes et absences.', + 'Cet espace vous permet de relier votre compte Exnet 3il à l\'application pour consulter vos notes et absences.', style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, 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 7aa7b72..693285b 100644 --- a/frontend/app_student/lib/api/users/entities/user_entity.dart +++ b/frontend/app_student/lib/api/users/entities/user_entity.dart @@ -1,9 +1,10 @@ class UserEntity { - final String ine; + final String? ine; final String firstName; - final DateTime birthDate; + final DateTime? birthDate; final String? className; + UserEntity( {required this.ine, required this.firstName, 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 1e1eb11..265cfbd 100644 --- a/frontend/app_student/lib/api/users/models/user_model.dart +++ b/frontend/app_student/lib/api/users/models/user_model.dart @@ -5,8 +5,9 @@ class UserModel { UserModel({required this.entity}); - String get file => entity.ine; + String get firstName => entity.firstName; String get name => entity.firstName; - DateTime get birthDate => entity.birthDate; + DateTime? get birthDate => entity.birthDate; String? get className => entity.className; + String? get ine => entity.ine; } 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 7932bdb..d6962a1 100644 --- a/frontend/app_student/lib/api/users/repositories/user_repository.dart +++ b/frontend/app_student/lib/api/users/repositories/user_repository.dart @@ -7,39 +7,47 @@ class UserRepository { Future getUser() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - String? ine = prefs.getString('ine'); String? name = prefs.getString('name'); - String? birthDate = prefs.getString('birthDate'); String? className = prefs.getString('className'); - if (ine != null && name != null && birthDate != null) { - return UserModel( - entity: UserEntity( - ine: ine, - firstName: name, - birthDate: DateTime.parse(birthDate), - className: className, - ), - ); - } else { - throw Exception('Utilisateur non trouvé'); + String? ine = prefs.getString('ine'); + String? birthDate = prefs.getString('birthDate'); + + if (name == null) { + throw Exception('User name not found'); } + + print('UserRepository.getUser: name: $name, className: $className, ine: $ine, birthDate: $birthDate'); + return UserModel( + entity: UserEntity( + firstName: name, + className: className, + ine: ine, + birthDate: birthDate != null ? DateTime.parse(birthDate) : null, + ), + ); } - Future saveUserDetails( - String ine, String name, String birthDate, String className) async { + Future saveUserDetails(String name, String? className, + {String? ine, String? birthDate}) async { SharedPreferences prefs = await SharedPreferences.getInstance(); UserModel( entity: UserEntity( - ine: ine, firstName: name, - birthDate: DateTime.parse(birthDate), className: className, + ine: ine, + birthDate: birthDate != null ? DateTime.parse(birthDate) : null, ), ); - await prefs.setString('ine', ine); await prefs.setString('name', name); - await prefs.setString('birthDate', birthDate); - await prefs.setString('className', className); + if (className != null) { + await prefs.setString('className', className); + } + if (ine != null) { + await prefs.setString('ine', ine); + } + if (birthDate != null) { + await prefs.setString('birthDate', birthDate); + } } Future deleteUser() async { diff --git a/frontend/app_student/lib/class_groups/views/class_group.dart b/frontend/app_student/lib/class_groups/views/class_group.dart index 3bea402..83f1b98 100644 --- a/frontend/app_student/lib/class_groups/views/class_group.dart +++ b/frontend/app_student/lib/class_groups/views/class_group.dart @@ -35,6 +35,7 @@ class ClassGroupPage extends StatelessWidget { appBar: const CustomAppBar(), body: BlocBuilder( builder: (context, userState) { + print(userState); if (userState is UserClassesSelected) { WidgetsBinding.instance.addPostFrameCallback((_) { context.go('/schedule'); @@ -55,9 +56,7 @@ class ClassGroupPage extends StatelessWidget { .classSelectionTitle(user.name)), HeaderSubtitle(AppLocalizations.of(context)! .classSelectionSubtitle), - Expanded( - child: CardList(classesList: classState.classes), - ), + CardList(classesList: classState.classes), ], ); } else if (classState is ClassGroupError) { 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 253c768..584f328 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 @@ -11,6 +11,7 @@ class CardList extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( + shrinkWrap: true, itemCount: classesList.length, itemBuilder: (context, index) { return Card( diff --git a/frontend/app_student/lib/login/cubit/login_cubit.dart b/frontend/app_student/lib/login/cubit/login_cubit.dart index 81cb043..eca525b 100644 --- a/frontend/app_student/lib/login/cubit/login_cubit.dart +++ b/frontend/app_student/lib/login/cubit/login_cubit.dart @@ -11,21 +11,19 @@ class LoginCubit extends Cubit { checkUserAuthentication(); } - Future areFieldsFilled( - String ine, String name, String birthDate) async { - if (ine.isEmpty || name.isEmpty || birthDate.isEmpty) { + Future areFieldsFilled(String name) async { + if (name.isEmpty) { return false; } return true; } - Future saveLoginDetails( - String ine, String name, String birthDate) async { - if (!(await areFieldsFilled(ine, name, birthDate))) { + Future saveLoginDetails(String name) async { + if (!(await areFieldsFilled(name))) { emit(LoginFieldError()); return; } - await userRepository.saveUserDetails(ine, name, birthDate, ''); + await userRepository.saveUserDetails(name, '', ine: null, birthDate: null); emit(RedirectToClassSelection()); } diff --git a/frontend/app_student/lib/login/views/login_view.dart b/frontend/app_student/lib/login/views/login_view.dart index 8634857..2cc5b8c 100644 --- a/frontend/app_student/lib/login/views/login_view.dart +++ b/frontend/app_student/lib/login/views/login_view.dart @@ -18,7 +18,7 @@ class LoginView extends StatelessWidget { body: Column( children: [ HeaderTitle(title), - const Expanded(child: FormLogin()), + FormLogin(), ], ), bottomBar: Padding( diff --git a/frontend/app_student/lib/login/views/widgets/form/button_submit.dart b/frontend/app_student/lib/login/views/widgets/form/button_submit.dart index 45789fa..dad0cee 100644 --- a/frontend/app_student/lib/login/views/widgets/form/button_submit.dart +++ b/frontend/app_student/lib/login/views/widgets/form/button_submit.dart @@ -5,17 +5,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class SubmitButton extends StatelessWidget { - final TextEditingController ineController; final TextEditingController nameController; - final TextEditingController birthDateController; - final DateTime birthDate; - const SubmitButton( - {super.key, - required this.ineController, - required this.nameController, - required this.birthDateController, - required this.birthDate}); + const SubmitButton({ + super.key, + required this.nameController, + }); @override Widget build(BuildContext context) { @@ -40,11 +35,9 @@ class SubmitButton extends StatelessWidget { ), ), onPressed: () { - final String ine = ineController.text.trim(); final String name = nameController.text.trim(); - final String birthDate = birthDateController.text.trim(); - context.read().saveLoginDetails(ine, name, birthDate); + context.read().saveLoginDetails(name); }, child: Text( AppLocalizations.of(context)!.loginButton, diff --git a/frontend/app_student/lib/login/views/widgets/form/form_login.dart b/frontend/app_student/lib/login/views/widgets/form/form_login.dart index 965ff4d..2241262 100644 --- a/frontend/app_student/lib/login/views/widgets/form/form_login.dart +++ b/frontend/app_student/lib/login/views/widgets/form/form_login.dart @@ -10,51 +10,21 @@ class FormLogin extends StatefulWidget { } class FormLoginState extends State { - final TextEditingController ineController = - TextEditingController(text: '999999999'); final TextEditingController nameController = TextEditingController(); - final TextEditingController birthDateController = - TextEditingController(text: '2000-01-01'); - DateTime birthDate = DateTime.now(); @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints viewportConstraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Stack( - children: [ - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 10.0), - const SizedBox(height: 10.0), - // ceci est temporaire, le laisser tant qu'on ne remet pas les autres inputs - FirstnameTextField(controller: nameController), - const SizedBox(height: 10.0), - ], - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: SubmitButton( - ineController: ineController, - nameController: nameController, - birthDateController: birthDateController, - birthDate: birthDate, - ), - ), - ], - ), - ), - ); - }, + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 10.0), + FirstnameTextField(controller: nameController), + const SizedBox(height: 30.0), + SubmitButton( + nameController: nameController, + ), + ], ); } } diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart index abdf9f0..980aab9 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -32,7 +32,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { final router = GoRouter( routes: AppRoutes.routes, - initialLocation: '/account', + initialLocation: '/login', errorPageBuilder: (context, state) { return MaterialPage( child: Scaffold( @@ -48,18 +48,7 @@ class MyApp extends StatelessWidget { return MaterialApp.router( debugShowCheckedModeBanner: false, title: '3iL Student App', - theme: ThemeData( - primarySwatch: Colors.blue, - appBarTheme: const AppBarTheme( - iconTheme: IconThemeData(color: Colors.white), - ), - // Orange 3IL - focusColor: CustomTheme.secondaryColor, - primaryColor: CustomTheme.primaryColor, - // Bleu 3IL - secondaryHeaderColor: CustomTheme.primaryColor, - fontFamily: 'Arial', - ), + theme: CustomTheme.theme, 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 7a3f6f9..924ca2c 100644 --- a/frontend/app_student/lib/menu/menu_view.dart +++ b/frontend/app_student/lib/menu/menu_view.dart @@ -1,9 +1,7 @@ import 'package:app_student/routes.dart'; 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:fluttertoast/fluttertoast.dart'; import 'package:go_router/go_router.dart'; import '../users/cubit/user_cubit.dart'; diff --git a/frontend/app_student/lib/profil/views/profil.dart b/frontend/app_student/lib/profil/views/profil.dart new file mode 100644 index 0000000..12eb55d --- /dev/null +++ b/frontend/app_student/lib/profil/views/profil.dart @@ -0,0 +1,110 @@ +import 'package:app_student/menu/menu_view.dart'; +import 'package:app_student/profil/views/widgets/class_group_button.dart'; +import 'package:app_student/profil/views/widgets/user_class_card.dart'; +import 'package:app_student/routes.dart'; +import 'package:app_student/utils/custom_layout.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; + +import '../../shared_components/header_logo.dart'; +import '../../shared_components/header_title.dart'; +import '../../users/cubit/user_cubit.dart'; +import '../../utils/global.dart'; +import 'widgets/user_info_card.dart'; + +import 'package:app_student/menu/menu_view.dart'; +import 'package:app_student/profil/views/widgets/class_group_button.dart'; +import 'package:app_student/profil/views/widgets/user_class_card.dart'; +import 'package:app_student/routes.dart'; +import 'package:app_student/utils/custom_layout.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; + +import '../../shared_components/header_logo.dart'; +import '../../shared_components/header_title.dart'; +import '../../users/cubit/user_cubit.dart'; +import '../../utils/global.dart'; +import 'widgets/user_info_card.dart'; + +class ProfilPage extends StatelessWidget { + const ProfilPage({super.key}); + + @override + Widget build(BuildContext context) { + return CustomLayout( + appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), + body: BlocBuilder( + builder: (context, state) { + print(state); + if (state is UserLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is UserPartialLoaded) { + final user = state.user; + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(left: 10.0), + child: HeaderTitle( + AppLocalizations.of(context)! + .profilMessageTitle(user.firstName ?? ''), + ), + ), + UserClassCard( + className: user.className ?? '-', + // Provide a default value if className is null + firstName: user.firstName ?? ''), + const ClassGroupButton(), + ], + ); + } else if (state is UserLoaded) { + final user = state.user; + + final birthDateString = + DateFormat('dd/MM/yyyy').format(user.birthDate!); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(left: 10.0), + child: HeaderTitle( + AppLocalizations.of(context)! + .profilMessageTitle(user.firstName), + ), + ), + UserClassCard( + className: user.className!, firstName: user.firstName), + UserInfoCard(ine: user.ine!, birthDate: birthDateString), + const ClassGroupButton(), + ], + ); + } else { + return const SizedBox.shrink(); + } + }, + ), + bottomContent: ElevatedButton( + onPressed: () { + context.read().deleteUser(); + GoRouter.of(context).go(AppRoutes.loginPage); + WidgetsBinding.instance.addPostFrameCallback((_) { + Fluttertoast.showToast( + msg: AppLocalizations.of(context)!.disconnectedMessage, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + textColor: Colors.white, + ); + }); + }, + child: Text(AppLocalizations.of(context)!.disconnect)), + bottomBar: const MenuBarView(), + ); + } +} diff --git a/frontend/app_student/lib/profils/views/widgets/class_group_button.dart b/frontend/app_student/lib/profil/views/widgets/class_group_button.dart similarity index 100% rename from frontend/app_student/lib/profils/views/widgets/class_group_button.dart rename to frontend/app_student/lib/profil/views/widgets/class_group_button.dart diff --git a/frontend/app_student/lib/profils/views/widgets/user_class_card.dart b/frontend/app_student/lib/profil/views/widgets/user_class_card.dart similarity index 100% rename from frontend/app_student/lib/profils/views/widgets/user_class_card.dart rename to frontend/app_student/lib/profil/views/widgets/user_class_card.dart diff --git a/frontend/app_student/lib/profils/views/widgets/user_info_card.dart b/frontend/app_student/lib/profil/views/widgets/user_info_card.dart similarity index 100% rename from frontend/app_student/lib/profils/views/widgets/user_info_card.dart rename to frontend/app_student/lib/profil/views/widgets/user_info_card.dart diff --git a/frontend/app_student/lib/profils/views/profil.dart b/frontend/app_student/lib/profils/views/profil.dart deleted file mode 100644 index 29aba02..0000000 --- a/frontend/app_student/lib/profils/views/profil.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:app_student/menu/menu_view.dart'; -import 'package:app_student/profils/views/widgets/class_group_button.dart'; -import 'package:app_student/profils/views/widgets/user_class_card.dart'; -import 'package:app_student/routes.dart'; -import 'package:app_student/utils/custom_layout.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:go_router/go_router.dart'; - -import '../../shared_components/header_logo.dart'; -import '../../shared_components/header_title.dart'; -import '../../users/cubit/user_cubit.dart'; -import '../../utils/global.dart'; - -class ProfilPage extends StatelessWidget { - const ProfilPage({super.key}); - - @override - Widget build(BuildContext context) { - final userState = context.watch().state; - String firstName = ''; - String className = ''; - // String ine = ''; - // DateTime? birthDate; - - if (userState is UserLoaded) { - firstName = userState.user.entity.firstName; - className = userState.user.entity.className!; - // ine = userState.user.entity.ine; - // birthDate = userState.user.entity.birthDate; - } - - // String birthDateString = birthDate != null - // ? DateFormat('dd/MM/yyyy').format(birthDate) - // : 'error'; - - return CustomLayout( - appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.only(left: 10.0), - child: HeaderTitle( - AppLocalizations.of(context)!.profilMessageTitle(firstName), - ), - ), - UserClassCard(className: className, firstName: firstName), - // UserInfoCard(ine: ine, birthDate: birthDateString), - const ClassGroupButton(), - ], - ), - bottomContent: ElevatedButton( - onPressed: () { - context.read().deleteUser(); - GoRouter.of(context).go(AppRoutes.loginPage); - WidgetsBinding.instance.addPostFrameCallback((_) { - Fluttertoast.showToast( - msg: AppLocalizations.of(context)!.disconnectedMessage, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - textColor: Colors.white, - ); - }); - }, - child: Text(AppLocalizations.of(context)!.disconnect)), - bottomBar: const MenuBarView(), - ); - } -} diff --git a/frontend/app_student/lib/routes.dart b/frontend/app_student/lib/routes.dart index 59e9a75..afe36b5 100644 --- a/frontend/app_student/lib/routes.dart +++ b/frontend/app_student/lib/routes.dart @@ -6,7 +6,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/profils/views/profil.dart'; +import 'package:app_student/profil/views/profil.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'; diff --git a/frontend/app_student/lib/users/cubit/user_cubit.dart b/frontend/app_student/lib/users/cubit/user_cubit.dart index 3350ddb..1b4ba0a 100644 --- a/frontend/app_student/lib/users/cubit/user_cubit.dart +++ b/frontend/app_student/lib/users/cubit/user_cubit.dart @@ -16,7 +16,11 @@ class UserCubit extends Cubit { try { emit(UserLoading()); final user = await userRepository.getUser(); - emit(UserLoaded(user)); + if (user.ine == null && user.birthDate == null) { + emit(UserPartialLoaded(user)); + } else { + emit(UserLoaded(user)); + } } catch (e) { emit(UserError(e.toString())); } diff --git a/frontend/app_student/lib/users/cubit/user_state.dart b/frontend/app_student/lib/users/cubit/user_state.dart index c08adbb..62ac575 100644 --- a/frontend/app_student/lib/users/cubit/user_state.dart +++ b/frontend/app_student/lib/users/cubit/user_state.dart @@ -7,12 +7,20 @@ class UserInitial extends UserState {} class UserLoading extends UserState {} + + class UserLoaded extends UserState { final UserModel user; UserLoaded(this.user); } +class UserPartialLoaded extends UserState { + final UserModel user; + + UserPartialLoaded(this.user); +} + class UserClassesSelected extends UserState {} class UserError extends UserState { diff --git a/frontend/app_student/lib/utils/custom_layout.dart b/frontend/app_student/lib/utils/custom_layout.dart index 0b3e841..817a62e 100644 --- a/frontend/app_student/lib/utils/custom_layout.dart +++ b/frontend/app_student/lib/utils/custom_layout.dart @@ -23,19 +23,14 @@ class CustomLayout extends StatelessWidget { padding: const EdgeInsets.only(left: 20.0, right: 20.0), child: LayoutBuilder( builder: (context, constraints) { - return Column( - children: [ - const SizedBox(height: 10), - Expanded( - child: body, - ), - if (bottomContent != null) - SizedBox( - height: constraints.maxHeight * 0.2, - // 20% of screen height - child: bottomContent, - ), - ], + return SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 10), + body, + if (bottomContent != null) bottomContent!, + ], + ), ); }, ), diff --git a/frontend/app_student/lib/utils/custom_theme.dart b/frontend/app_student/lib/utils/custom_theme.dart index 7de4629..2e61504 100644 --- a/frontend/app_student/lib/utils/custom_theme.dart +++ b/frontend/app_student/lib/utils/custom_theme.dart @@ -13,6 +13,18 @@ class CustomTheme { static const TextStyle text = TextStyle(fontSize: 16); static const TextStyle textSmall = TextStyle(fontSize: 14); static const TextStyle textXs = TextStyle(fontSize: 12); + + static ThemeData get theme { + return ThemeData( + appBarTheme: const AppBarTheme( + iconTheme: IconThemeData(color: Colors.white), + ), + focusColor: secondaryColor, + primaryColor: primaryColor, + secondaryHeaderColor: primaryColor, + fontFamily: 'Arial', + ); + } } extension TextStyleHelpers on TextStyle { From 4f0362f183c4fcfdf1d5d29db96225bbb90dd6b9 Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 25 Apr 2024 14:03:16 +0200 Subject: [PATCH 4/7] :lipstick: : custom_button, pdf_view --- frontend/app_student/.env.dist | 4 +- frontend/app_student/.tool-versions | 1 + .../lib/account/account_cubit.dart | 5 -- .../lib/account/views/account.dart | 4 +- .../account/views/widgets/account_dialog.dart | 2 +- .../lib/account/views/widgets/pdf_card.dart | 6 +- .../lib/account/views/widgets/pdf_view.dart | 2 +- .../lib/api/users/entities/user_entity.dart | 1 - .../users/repositories/user_repository.dart | 1 - .../lib/class_groups/views/class_group.dart | 3 +- .../lib/login/views/login_view.dart | 28 +++++++- .../views/widgets/form/button_submit.dart | 40 +++-------- .../login/views/widgets/form/form_login.dart | 29 -------- frontend/app_student/lib/menu/menu_view.dart | 2 - .../app_student/lib/profil/views/profil.dart | 72 ++++++++----------- .../profil/views/widgets/user_class_card.dart | 42 ++++++----- .../lib/shared_components/app_bar.dart | 2 +- .../lib/shared_components/header_logo.dart | 15 ++-- .../lib/users/cubit/user_state.dart | 2 - .../app_student/lib/utils/custom_button.dart | 41 +++++++++++ .../app_student/lib/utils/custom_layout.dart | 29 +++++--- frontend/app_student/lib/utils/global.dart | 2 +- 22 files changed, 174 insertions(+), 159 deletions(-) create mode 100644 frontend/app_student/.tool-versions create mode 100644 frontend/app_student/lib/utils/custom_button.dart diff --git a/frontend/app_student/.env.dist b/frontend/app_student/.env.dist index 4873f58..317cd49 100644 --- a/frontend/app_student/.env.dist +++ b/frontend/app_student/.env.dist @@ -1,2 +1,2 @@ -DEV_API_URL= -PROD_API_URL= \ No newline at end of file +DEV_API_URL=https://api-dev.lukasvalois.com +PROD_API_URL=https://api.lukasvalois.com \ No newline at end of file diff --git a/frontend/app_student/.tool-versions b/frontend/app_student/.tool-versions new file mode 100644 index 0000000..99dfa87 --- /dev/null +++ b/frontend/app_student/.tool-versions @@ -0,0 +1 @@ +flutter 3.19.2 diff --git a/frontend/app_student/lib/account/account_cubit.dart b/frontend/app_student/lib/account/account_cubit.dart index 5e5ae58..134f0b7 100644 --- a/frontend/app_student/lib/account/account_cubit.dart +++ b/frontend/app_student/lib/account/account_cubit.dart @@ -1,11 +1,8 @@ - import 'dart:io'; - import 'package:app_student/utils/global.dart'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; import 'package:app_student/api/account/repositories/account_repository.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'account_state.dart'; @@ -49,6 +46,4 @@ class AccountCubit extends Cubit { emit(AccountInitial()); } } - - } diff --git a/frontend/app_student/lib/account/views/account.dart b/frontend/app_student/lib/account/views/account.dart index 3c7b41e..51475db 100644 --- a/frontend/app_student/lib/account/views/account.dart +++ b/frontend/app_student/lib/account/views/account.dart @@ -16,10 +16,10 @@ class AccountPage extends StatefulWidget { const AccountPage({super.key}); @override - _AccountPageState createState() => _AccountPageState(); + AccountPageState createState() => AccountPageState(); } -class _AccountPageState extends State { +class AccountPageState extends State { late final AccountCubit accountCubit; @override diff --git a/frontend/app_student/lib/account/views/widgets/account_dialog.dart b/frontend/app_student/lib/account/views/widgets/account_dialog.dart index ce7b1fe..33d0ac3 100644 --- a/frontend/app_student/lib/account/views/widgets/account_dialog.dart +++ b/frontend/app_student/lib/account/views/widgets/account_dialog.dart @@ -60,4 +60,4 @@ class AccountDialog extends StatelessWidget { ], ); } -} \ No newline at end of file +} diff --git a/frontend/app_student/lib/account/views/widgets/pdf_card.dart b/frontend/app_student/lib/account/views/widgets/pdf_card.dart index cf36400..0586742 100644 --- a/frontend/app_student/lib/account/views/widgets/pdf_card.dart +++ b/frontend/app_student/lib/account/views/widgets/pdf_card.dart @@ -6,7 +6,7 @@ class PdfCard extends StatelessWidget { final String filePath; final String title; - const PdfCard({required this.filePath, required this.title}); + const PdfCard({super.key, required this.filePath, required this.title}); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PdfCard extends StatelessWidget { color: Colors.black.withOpacity(0.2), spreadRadius: 1, blurRadius: 2, - offset: Offset(0, 1), + offset: const Offset(0, 1), ), ], ), @@ -43,4 +43,4 @@ class PdfCard extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/frontend/app_student/lib/account/views/widgets/pdf_view.dart b/frontend/app_student/lib/account/views/widgets/pdf_view.dart index caa3bac..d6c873b 100644 --- a/frontend/app_student/lib/account/views/widgets/pdf_view.dart +++ b/frontend/app_student/lib/account/views/widgets/pdf_view.dart @@ -24,4 +24,4 @@ class PdfViewPage extends StatelessWidget { body: PDFView(filePath: filePath), ); } -} \ No newline at end of file +} diff --git a/frontend/app_student/lib/api/users/entities/user_entity.dart b/frontend/app_student/lib/api/users/entities/user_entity.dart index 693285b..c559ea7 100644 --- a/frontend/app_student/lib/api/users/entities/user_entity.dart +++ b/frontend/app_student/lib/api/users/entities/user_entity.dart @@ -4,7 +4,6 @@ class UserEntity { final DateTime? birthDate; final String? className; - UserEntity( {required this.ine, required this.firstName, 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 d6962a1..f541c38 100644 --- a/frontend/app_student/lib/api/users/repositories/user_repository.dart +++ b/frontend/app_student/lib/api/users/repositories/user_repository.dart @@ -16,7 +16,6 @@ class UserRepository { throw Exception('User name not found'); } - print('UserRepository.getUser: name: $name, className: $className, ine: $ine, birthDate: $birthDate'); return UserModel( entity: UserEntity( firstName: name, diff --git a/frontend/app_student/lib/class_groups/views/class_group.dart b/frontend/app_student/lib/class_groups/views/class_group.dart index 83f1b98..065962a 100644 --- a/frontend/app_student/lib/class_groups/views/class_group.dart +++ b/frontend/app_student/lib/class_groups/views/class_group.dart @@ -35,7 +35,6 @@ class ClassGroupPage extends StatelessWidget { appBar: const CustomAppBar(), body: BlocBuilder( builder: (context, userState) { - print(userState); if (userState is UserClassesSelected) { WidgetsBinding.instance.addPostFrameCallback((_) { context.go('/schedule'); @@ -43,7 +42,7 @@ class ClassGroupPage extends StatelessWidget { return const Center(child: CircularProgressIndicator()); } else if (userState is UserLoading) { return const Center(child: CircularProgressIndicator()); - } else if (userState is UserLoaded) { + } else if (userState is UserPartialLoaded) { final user = userState.user; return BlocBuilder( builder: (context, classState) { diff --git a/frontend/app_student/lib/login/views/login_view.dart b/frontend/app_student/lib/login/views/login_view.dart index 2cc5b8c..a2f0d99 100644 --- a/frontend/app_student/lib/login/views/login_view.dart +++ b/frontend/app_student/lib/login/views/login_view.dart @@ -1,15 +1,17 @@ -import 'package:app_student/login/views/widgets/form/form_login.dart'; import 'package:app_student/utils/custom_layout.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import '../../shared_components/header_logo.dart'; import '../../shared_components/header_title.dart'; import '../../utils/global.dart'; +import 'widgets/form/button_submit.dart'; +import 'widgets/form/inputs/input_prenom.dart'; class LoginView extends StatelessWidget { final String title; + final TextEditingController nameController = TextEditingController(); - const LoginView(this.title, {super.key}); + LoginView(this.title, {super.key}); @override Widget build(BuildContext context) { @@ -18,9 +20,10 @@ class LoginView extends StatelessWidget { body: Column( children: [ HeaderTitle(title), - FormLogin(), + FormLogin(nameController: nameController), ], ), + bottomContent: SubmitButton(nameController: nameController), bottomBar: Padding( padding: const EdgeInsets.all(10.0), child: Text( @@ -35,3 +38,22 @@ class LoginView extends StatelessWidget { ); } } + +class FormLogin extends StatelessWidget { + final TextEditingController nameController; + + const FormLogin({required this.nameController, super.key}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 10.0), + FirstnameTextField(controller: nameController), + const SizedBox(height: 30.0), + ], + ); + } +} diff --git a/frontend/app_student/lib/login/views/widgets/form/button_submit.dart b/frontend/app_student/lib/login/views/widgets/form/button_submit.dart index dad0cee..dbccac7 100644 --- a/frontend/app_student/lib/login/views/widgets/form/button_submit.dart +++ b/frontend/app_student/lib/login/views/widgets/form/button_submit.dart @@ -1,4 +1,5 @@ import 'package:app_student/login/cubit/login_cubit.dart'; +import 'package:app_student/utils/custom_button.dart'; import 'package:app_student/utils/custom_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -14,37 +15,14 @@ class SubmitButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: double.infinity, - height: 50.0, - child: ElevatedButton( - style: ButtonStyle( - textStyle: MaterialStateProperty.all( - CustomTheme.text.toBold, - ), - backgroundColor: - MaterialStateProperty.all(CustomTheme.secondaryColor), - foregroundColor: MaterialStateProperty.all(Colors.white), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(3.0), - ), - ), - ), - onPressed: () { - final String name = nameController.text.trim(); - - context.read().saveLoginDetails(name); - }, - child: Text( - AppLocalizations.of(context)!.loginButton, - ), - ), - ), - ], + return CustomButton( + text: AppLocalizations.of(context)!.loginButton, + onPressed: () { + final String name = nameController.text.trim(); + context.read().saveLoginDetails(name); + }, + backgroundColor: CustomTheme.secondaryColor, + textColor: Colors.white, ); } } diff --git a/frontend/app_student/lib/login/views/widgets/form/form_login.dart b/frontend/app_student/lib/login/views/widgets/form/form_login.dart index 2241262..8b13789 100644 --- a/frontend/app_student/lib/login/views/widgets/form/form_login.dart +++ b/frontend/app_student/lib/login/views/widgets/form/form_login.dart @@ -1,30 +1 @@ -import 'package:flutter/material.dart'; -import 'button_submit.dart'; -import 'inputs/input_prenom.dart'; -class FormLogin extends StatefulWidget { - const FormLogin({super.key}); - - @override - FormLoginState createState() => FormLoginState(); -} - -class FormLoginState extends State { - final TextEditingController nameController = TextEditingController(); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 10.0), - FirstnameTextField(controller: nameController), - const SizedBox(height: 30.0), - SubmitButton( - nameController: nameController, - ), - ], - ); - } -} diff --git a/frontend/app_student/lib/menu/menu_view.dart b/frontend/app_student/lib/menu/menu_view.dart index 924ca2c..cf1669a 100644 --- a/frontend/app_student/lib/menu/menu_view.dart +++ b/frontend/app_student/lib/menu/menu_view.dart @@ -3,8 +3,6 @@ import 'package:app_student/utils/custom_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; - -import '../users/cubit/user_cubit.dart'; import 'menu_item.dart'; class MenuBarView extends StatefulWidget { diff --git a/frontend/app_student/lib/profil/views/profil.dart b/frontend/app_student/lib/profil/views/profil.dart index 12eb55d..c52681a 100644 --- a/frontend/app_student/lib/profil/views/profil.dart +++ b/frontend/app_student/lib/profil/views/profil.dart @@ -1,23 +1,4 @@ import 'package:app_student/menu/menu_view.dart'; -import 'package:app_student/profil/views/widgets/class_group_button.dart'; -import 'package:app_student/profil/views/widgets/user_class_card.dart'; -import 'package:app_student/routes.dart'; -import 'package:app_student/utils/custom_layout.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; - -import '../../shared_components/header_logo.dart'; -import '../../shared_components/header_title.dart'; -import '../../users/cubit/user_cubit.dart'; -import '../../utils/global.dart'; -import 'widgets/user_info_card.dart'; - -import 'package:app_student/menu/menu_view.dart'; -import 'package:app_student/profil/views/widgets/class_group_button.dart'; import 'package:app_student/profil/views/widgets/user_class_card.dart'; import 'package:app_student/routes.dart'; import 'package:app_student/utils/custom_layout.dart'; @@ -31,6 +12,7 @@ import 'package:intl/intl.dart'; import '../../shared_components/header_logo.dart'; import '../../shared_components/header_title.dart'; import '../../users/cubit/user_cubit.dart'; +import '../../utils/custom_button.dart'; import '../../utils/global.dart'; import 'widgets/user_info_card.dart'; @@ -43,7 +25,6 @@ class ProfilPage extends StatelessWidget { appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), body: BlocBuilder( builder: (context, state) { - print(state); if (state is UserLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is UserPartialLoaded) { @@ -54,14 +35,17 @@ 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 ?? '-', - // Provide a default value if className is null - firstName: user.firstName ?? ''), - const ClassGroupButton(), + className: user.className!, + firstName: user.firstName, + onTap: () { + context.read().clearUserClass(); + GoRouter.of(context).go(AppRoutes.classListPage); + }, + ), ], ); } else if (state is UserLoaded) { @@ -80,9 +64,14 @@ class ProfilPage extends StatelessWidget { ), ), UserClassCard( - className: user.className!, firstName: user.firstName), + className: user.className!, + firstName: user.firstName, + onTap: () { + context.read().clearUserClass(); + GoRouter.of(context).go(AppRoutes.classListPage); + }, + ), UserInfoCard(ine: user.ine!, birthDate: birthDateString), - const ClassGroupButton(), ], ); } else { @@ -90,20 +79,21 @@ class ProfilPage extends StatelessWidget { } }, ), - bottomContent: ElevatedButton( - onPressed: () { - context.read().deleteUser(); - GoRouter.of(context).go(AppRoutes.loginPage); - WidgetsBinding.instance.addPostFrameCallback((_) { - Fluttertoast.showToast( - msg: AppLocalizations.of(context)!.disconnectedMessage, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - textColor: Colors.white, - ); - }); - }, - child: Text(AppLocalizations.of(context)!.disconnect)), + bottomContent: CustomButton( + text: AppLocalizations.of(context)!.disconnect, + onPressed: () { + context.read().deleteUser(); + GoRouter.of(context).go(AppRoutes.loginPage); + WidgetsBinding.instance.addPostFrameCallback((_) { + Fluttertoast.showToast( + msg: AppLocalizations.of(context)!.disconnectedMessage, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + textColor: Colors.white, + ); + }); + }, + ), bottomBar: const MenuBarView(), ); } diff --git a/frontend/app_student/lib/profil/views/widgets/user_class_card.dart b/frontend/app_student/lib/profil/views/widgets/user_class_card.dart index db64c2f..1c0d896 100644 --- a/frontend/app_student/lib/profil/views/widgets/user_class_card.dart +++ b/frontend/app_student/lib/profil/views/widgets/user_class_card.dart @@ -5,30 +5,38 @@ import 'package:flutter_svg/svg.dart'; class UserClassCard extends StatelessWidget { final String className; final String firstName; + final VoidCallback onTap; - const UserClassCard( - {super.key, required this.className, required this.firstName}); + const UserClassCard({ + required this.className, + required this.firstName, + required this.onTap, + super.key, + }); @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(10.0), - child: Card( - child: ListTile( - leading: SizedBox( - width: 50, - child: ColorFiltered( - colorFilter: const ColorFilter.mode( - CustomTheme.primaryColor, BlendMode.srcIn), - child: SvgPicture.asset( - 'assets/images/user.svg', - width: 30, - height: 30, + return GestureDetector( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Card( + child: ListTile( + leading: SizedBox( + width: 50, + child: ColorFiltered( + colorFilter: const ColorFilter.mode( + CustomTheme.primaryColor, BlendMode.srcIn), + child: SvgPicture.asset( + 'assets/images/user.svg', + width: 30, + height: 30, + ), ), ), + title: Text(className, style: CustomTheme.textXl), + subtitle: Text(firstName, style: CustomTheme.text), ), - title: Text(className, style: CustomTheme.textXl), - subtitle: Text(firstName, style: CustomTheme.text), ), ), ); diff --git a/frontend/app_student/lib/shared_components/app_bar.dart b/frontend/app_student/lib/shared_components/app_bar.dart index d62aa88..e12d638 100644 --- a/frontend/app_student/lib/shared_components/app_bar.dart +++ b/frontend/app_student/lib/shared_components/app_bar.dart @@ -15,7 +15,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { final userState = context.watch().state; String className = ''; - if (userState is UserLoaded) { + if (userState is UserPartialLoaded) { className = userState.user.className ?? ''; } return AppBar( diff --git a/frontend/app_student/lib/shared_components/header_logo.dart b/frontend/app_student/lib/shared_components/header_logo.dart index dacf29c..ee38737 100644 --- a/frontend/app_student/lib/shared_components/header_logo.dart +++ b/frontend/app_student/lib/shared_components/header_logo.dart @@ -5,16 +5,19 @@ class HeaderLogo extends StatelessWidget implements PreferredSizeWidget { final double appBarHeight; final bool showBackButton; - const HeaderLogo({super.key, required this.appBarHeight, this.showBackButton = false}); + const HeaderLogo( + {super.key, required this.appBarHeight, this.showBackButton = false}); @override Widget build(BuildContext context) { return AppBar( backgroundColor: CustomTheme.primaryColor, - leading: showBackButton ? IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => Navigator.of(context).pop(), - ) : null, + leading: showBackButton + ? IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.of(context).pop(), + ) + : null, flexibleSpace: Center( child: SizedBox( width: 200.0, @@ -29,4 +32,4 @@ class HeaderLogo extends StatelessWidget implements PreferredSizeWidget { Size get preferredSize { return Size.fromHeight(appBarHeight); } -} \ No newline at end of file +} diff --git a/frontend/app_student/lib/users/cubit/user_state.dart b/frontend/app_student/lib/users/cubit/user_state.dart index 62ac575..f706bc9 100644 --- a/frontend/app_student/lib/users/cubit/user_state.dart +++ b/frontend/app_student/lib/users/cubit/user_state.dart @@ -7,8 +7,6 @@ class UserInitial extends UserState {} class UserLoading extends UserState {} - - class UserLoaded extends UserState { final UserModel user; diff --git a/frontend/app_student/lib/utils/custom_button.dart b/frontend/app_student/lib/utils/custom_button.dart new file mode 100644 index 0000000..e279555 --- /dev/null +++ b/frontend/app_student/lib/utils/custom_button.dart @@ -0,0 +1,41 @@ +import 'package:app_student/utils/custom_theme.dart'; +import 'package:flutter/material.dart'; + +class CustomButton extends StatelessWidget { + final String text; + final VoidCallback onPressed; + final Color backgroundColor; + final Color textColor; + + const CustomButton({ + required this.text, + required this.onPressed, + this.backgroundColor = CustomTheme.secondaryColor, + this.textColor = Colors.white, + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + height: 50.0, + child: ElevatedButton( + style: ButtonStyle( + textStyle: MaterialStateProperty.all( + CustomTheme.text.toBold, + ), + backgroundColor: MaterialStateProperty.all(backgroundColor), + foregroundColor: MaterialStateProperty.all(textColor), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + ), + ), + ), + onPressed: onPressed, + child: Text(text, style: CustomTheme.textSmall.toColorWhite), + ), + ); + } +} diff --git a/frontend/app_student/lib/utils/custom_layout.dart b/frontend/app_student/lib/utils/custom_layout.dart index 817a62e..ac24cf7 100644 --- a/frontend/app_student/lib/utils/custom_layout.dart +++ b/frontend/app_student/lib/utils/custom_layout.dart @@ -23,14 +23,27 @@ class CustomLayout extends StatelessWidget { padding: const EdgeInsets.only(left: 20.0, right: 20.0), child: LayoutBuilder( builder: (context, constraints) { - return SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 10), - body, - if (bottomContent != null) bottomContent!, - ], - ), + return Stack( + children: [ + SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 10), + body, + if (bottomContent != null) + SizedBox( + height: MediaQuery.of(context).size.height * 0.3), + ], + ), + ), + if (bottomContent != null) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: bottomContent!, + ), + ], ); }, ), diff --git a/frontend/app_student/lib/utils/global.dart b/frontend/app_student/lib/utils/global.dart index 8965907..f959352 100644 --- a/frontend/app_student/lib/utils/global.dart +++ b/frontend/app_student/lib/utils/global.dart @@ -23,4 +23,4 @@ class Global { final prefs = await SharedPreferences.getInstance(); prefs.setInt('studentId', value); } -} \ No newline at end of file +} From 724123533480f512d86a13cd1fa39668b333f02f Mon Sep 17 00:00:00 2001 From: Jules Artaud Date: Sat, 27 Apr 2024 18:05:13 +0200 Subject: [PATCH 5/7] :construction: user state to fecth pdf --- .../lib/account/account_cubit.dart | 2 + .../lib/account/views/account.dart | 25 +++--- .../views/forms/account_link_form.dart | 7 +- .../account/views/widgets/account_dialog.dart | 63 ------------- .../lib/api/users/entities/user_entity.dart | 16 +++- .../lib/api/users/models/user_model.dart | 16 +++- .../users/repositories/user_repository.dart | 89 +++++++++++++++++-- .../lib/class_groups/views/class_group.dart | 2 +- .../app_student/lib/profil/views/profil.dart | 10 ++- frontend/app_student/lib/routes.dart | 20 ++++- .../lib/shared_components/app_bar.dart | 6 +- .../lib/users/cubit/user_cubit.dart | 52 ++++++++++- .../lib/users/cubit/user_state.dart | 16 +++- .../app_student/lib/utils/custom_layout.dart | 2 +- frontend/app_student/lib/utils/global.dart | 20 +++++ 15 files changed, 239 insertions(+), 107 deletions(-) delete mode 100644 frontend/app_student/lib/account/views/widgets/account_dialog.dart diff --git a/frontend/app_student/lib/account/account_cubit.dart b/frontend/app_student/lib/account/account_cubit.dart index 134f0b7..47bf8a3 100644 --- a/frontend/app_student/lib/account/account_cubit.dart +++ b/frontend/app_student/lib/account/account_cubit.dart @@ -14,6 +14,8 @@ class AccountCubit extends Cubit { Future loginAndSaveId(String username, String password) async { emit(AccountLoading()); try { + Global.setIne(username); + Global.setBirthDate(password); final studentId = await accountRepository.login(username, password); emit(AccountLoggedIn(studentId: studentId)); } catch (e) { diff --git a/frontend/app_student/lib/account/views/account.dart b/frontend/app_student/lib/account/views/account.dart index 51475db..007f82b 100644 --- a/frontend/app_student/lib/account/views/account.dart +++ b/frontend/app_student/lib/account/views/account.dart @@ -1,5 +1,6 @@ import 'package:app_student/account/views/forms/account_link_form.dart'; import 'package:app_student/account/views/widgets/pdf_card.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:app_student/utils/custom_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -20,17 +21,17 @@ class AccountPage extends StatefulWidget { } class AccountPageState extends State { - late final AccountCubit accountCubit; + late final UserCubit userCubit; @override void initState() { super.initState(); - accountCubit = BlocProvider.of(context); - accountCubit.checkStudentId(); + userCubit = BlocProvider.of(context); + userCubit.checkStudentId(); - accountCubit.stream.listen((state) { + userCubit.stream.listen((state) { if (state is AccountLoggedIn) { - accountCubit.fetchMarks(); + userCubit.fetchMarks(); } }); } @@ -39,11 +40,11 @@ class AccountPageState extends State { Widget build(BuildContext context) { return CustomLayout( appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), - body: BlocBuilder( + body: BlocBuilder( builder: (context, state) { - if (state is AccountLoading) { + if (state is UserLoading) { return const Center(child: CircularProgressIndicator()); - } else if (state is AccountLoaded) { + } else if (state is UserLoaded) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -64,14 +65,14 @@ class AccountPageState extends State { ), const SizedBox(height: 40), PdfCard( - filePath: state.marksFile.path, title: 'Relevé de notes'), + filePath: state.user.marksFile!.path, title: 'Relevé de notes'), const SizedBox(height: 20), PdfCard( - filePath: state.absencesFile.path, + filePath: state.user.absencesFile!.path, title: "Relevé d'absences"), ], ); - } else if (state is AccountInitial) { + } else if (state is UserInitial) { return Column( children: [ const Padding( @@ -100,7 +101,7 @@ class AccountPageState extends State { context, MaterialPageRoute( builder: (context) => - LoginFormPage(accountCubit: accountCubit), + LoginFormPage(userCubit: userCubit), ), ); }, diff --git a/frontend/app_student/lib/account/views/forms/account_link_form.dart b/frontend/app_student/lib/account/views/forms/account_link_form.dart index 4642949..210f60a 100644 --- a/frontend/app_student/lib/account/views/forms/account_link_form.dart +++ b/frontend/app_student/lib/account/views/forms/account_link_form.dart @@ -1,13 +1,14 @@ import 'package:app_student/account/account_cubit.dart'; import 'package:app_student/shared_components/header_logo.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; import 'package:app_student/utils/custom_layout.dart'; import 'package:app_student/utils/global.dart'; import 'package:flutter/material.dart'; class LoginFormPage extends StatelessWidget { - final AccountCubit accountCubit; + final UserCubit userCubit; - const LoginFormPage({super.key, required this.accountCubit}); + const LoginFormPage({super.key, required this.userCubit}); @override Widget build(BuildContext context) { @@ -52,7 +53,7 @@ class LoginFormPage extends StatelessWidget { onPressed: () { if (formKey.currentState!.validate()) { formKey.currentState!.save(); - accountCubit.loginAndSaveId(ine, birthDate); + userCubit.loginAndSaveId(ine, birthDate); Navigator.of(context).pop(); } }, diff --git a/frontend/app_student/lib/account/views/widgets/account_dialog.dart b/frontend/app_student/lib/account/views/widgets/account_dialog.dart deleted file mode 100644 index 33d0ac3..0000000 --- a/frontend/app_student/lib/account/views/widgets/account_dialog.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:app_student/account/account_cubit.dart'; -import 'package:flutter/material.dart'; - -class AccountDialog extends StatelessWidget { - final AccountCubit accountCubit; - - const AccountDialog({super.key, required this.accountCubit}); - - @override - Widget build(BuildContext context) { - final formKey = GlobalKey(); - String ine = ''; - String birthDate = ''; - - return AlertDialog( - title: const Text('Login'), - content: Form( - key: formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - decoration: const InputDecoration(labelText: 'INE'), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your INE'; - } - return null; - }, - onSaved: (value) { - ine = value!; - }, - ), - TextFormField( - decoration: const InputDecoration(labelText: 'Birth Date'), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your birth date'; - } - return null; - }, - onSaved: (value) { - birthDate = value!; - }, - ), - ], - ), - ), - actions: [ - TextButton( - child: const Text('Submit'), - onPressed: () { - if (formKey.currentState!.validate()) { - formKey.currentState!.save(); - accountCubit.loginAndSaveId(ine, birthDate); - Navigator.of(context).pop(); - } - }, - ), - ], - ); - } -} 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 c559ea7..e404009 100644 --- a/frontend/app_student/lib/api/users/entities/user_entity.dart +++ b/frontend/app_student/lib/api/users/entities/user_entity.dart @@ -1,12 +1,20 @@ +import 'dart:io'; + class UserEntity { final String? ine; final String firstName; final DateTime? birthDate; final String? className; + final int? studentId; + final File? marksFile; + final File? absencesFile; UserEntity( {required this.ine, - required this.firstName, - required this.birthDate, - this.className}); -} + required this.firstName, + required this.birthDate, + this.className, + this.studentId, + this.marksFile, + this.absencesFile}); +} \ No newline at end of file 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 265cfbd..de9f301 100644 --- a/frontend/app_student/lib/api/users/models/user_model.dart +++ b/frontend/app_student/lib/api/users/models/user_model.dart @@ -1,4 +1,6 @@ -import '../entities/user_entity.dart'; +import 'dart:io'; + +import 'package:app_student/api/users/entities/user_entity.dart'; class UserModel { final UserEntity entity; @@ -6,8 +8,18 @@ class UserModel { UserModel({required this.entity}); String get firstName => entity.firstName; + String get name => entity.firstName; + DateTime? get birthDate => entity.birthDate; + String? get className => entity.className; + String? get ine => entity.ine; -} + + int? get studentId => entity.studentId; + + File? get marksFile => entity.marksFile; + + File? get absencesFile => entity.absencesFile; +} \ No newline at end of file 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 f541c38..4e7a058 100644 --- a/frontend/app_student/lib/api/users/repositories/user_repository.dart +++ b/frontend/app_student/lib/api/users/repositories/user_repository.dart @@ -1,9 +1,19 @@ +import 'dart:convert'; +import 'dart:io'; 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 '../entities/user_entity.dart'; +import 'package:http/http.dart' as http; class UserRepository { + final ApiService apiService; + + UserRepository({required this.apiService}); + Future getUser() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -11,18 +21,34 @@ class UserRepository { String? className = prefs.getString('className'); String? ine = prefs.getString('ine'); String? birthDate = prefs.getString('birthDate'); + int? studentId = prefs.getInt('studentId'); + + 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: birthDate != null ? DateTime.parse(birthDate) : null, - ), + firstName: name, + className: className, + ine: ine, + birthDate: bd, + studentId: studentId, + marksFile: marksFile, + absencesFile: absencesFile), ); } @@ -75,4 +101,55 @@ class UserRepository { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.remove('className'); } + + Future 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/class_groups/views/class_group.dart b/frontend/app_student/lib/class_groups/views/class_group.dart index 065962a..250275e 100644 --- a/frontend/app_student/lib/class_groups/views/class_group.dart +++ b/frontend/app_student/lib/class_groups/views/class_group.dart @@ -42,7 +42,7 @@ class ClassGroupPage extends StatelessWidget { return const Center(child: CircularProgressIndicator()); } else if (userState is UserLoading) { return const Center(child: CircularProgressIndicator()); - } else if (userState is UserPartialLoaded) { + } else if (userState is UserWithoutClass) { final user = userState.user; return BlocBuilder( builder: (context, classState) { diff --git a/frontend/app_student/lib/profil/views/profil.dart b/frontend/app_student/lib/profil/views/profil.dart index c52681a..636905c 100644 --- a/frontend/app_student/lib/profil/views/profil.dart +++ b/frontend/app_student/lib/profil/views/profil.dart @@ -25,9 +25,10 @@ class ProfilPage extends StatelessWidget { appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), body: BlocBuilder( builder: (context, state) { + if (state is UserLoading) { return const Center(child: CircularProgressIndicator()); - } else if (state is UserPartialLoaded) { + } else if (state is UserNameLoaded) { final user = state.user; return Column( children: [ @@ -81,9 +82,10 @@ class ProfilPage extends StatelessWidget { ), bottomContent: CustomButton( text: AppLocalizations.of(context)!.disconnect, - onPressed: () { - context.read().deleteUser(); - GoRouter.of(context).go(AppRoutes.loginPage); + onPressed: () async { + final goRouter = GoRouter.of(context); + await context.read().deleteUser(); + goRouter.go(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 afe36b5..6197b5a 100644 --- a/frontend/app_student/lib/routes.dart +++ b/frontend/app_student/lib/routes.dart @@ -38,7 +38,10 @@ class AppRoutes { create: (context) => ClassGroupRepository( apiService: ApiService(apiUrl: context.read().apiUrl))), - RepositoryProvider(create: (context) => UserRepository()), + RepositoryProvider( + create: (context) => UserRepository( + apiService: + ApiService(apiUrl: context.read().apiUrl))), ], child: MultiBlocProvider(providers: [ BlocProvider( @@ -61,7 +64,10 @@ class AppRoutes { key: state.pageKey, child: MultiRepositoryProvider( providers: [ - RepositoryProvider(create: (context) => UserRepository()), + RepositoryProvider( + create: (context) => UserRepository( + apiService: + ApiService(apiUrl: context.read().apiUrl))), ], child: MultiBlocProvider( providers: [ @@ -89,7 +95,10 @@ class AppRoutes { create: (context) => WeekScheduleRepository( apiService: ApiService(apiUrl: context.read().apiUrl))), - RepositoryProvider(create: (context) => UserRepository()), + RepositoryProvider( + create: (context) => UserRepository( + apiService: + ApiService(apiUrl: context.read().apiUrl))), ], child: MultiBlocProvider( providers: [ @@ -116,7 +125,10 @@ class AppRoutes { key: state.pageKey, child: MultiRepositoryProvider( providers: [ - RepositoryProvider(create: (context) => UserRepository()), + RepositoryProvider( + create: (context) => UserRepository( + apiService: + ApiService(apiUrl: context.read().apiUrl))), ], child: MultiBlocProvider( providers: [ diff --git a/frontend/app_student/lib/shared_components/app_bar.dart b/frontend/app_student/lib/shared_components/app_bar.dart index e12d638..908c5e3 100644 --- a/frontend/app_student/lib/shared_components/app_bar.dart +++ b/frontend/app_student/lib/shared_components/app_bar.dart @@ -15,8 +15,10 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { final userState = context.watch().state; String className = ''; - if (userState is UserPartialLoaded) { - className = userState.user.className ?? ''; + if (userState is UserLoaded) { + className = (userState).user.className ?? ''; + } else if (userState is UserNameLoaded) { + className = (userState).user.className ?? ''; } return AppBar( backgroundColor: CustomTheme.primaryColor, diff --git a/frontend/app_student/lib/users/cubit/user_cubit.dart b/frontend/app_student/lib/users/cubit/user_cubit.dart index 1b4ba0a..7700485 100644 --- a/frontend/app_student/lib/users/cubit/user_cubit.dart +++ b/frontend/app_student/lib/users/cubit/user_cubit.dart @@ -1,9 +1,12 @@ +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'; import 'package:meta/meta.dart'; import '../../api/users/repositories/user_repository.dart'; +import '../../utils/global.dart'; part 'user_state.dart'; @@ -16,10 +19,15 @@ class UserCubit extends Cubit { try { emit(UserLoading()); final user = await userRepository.getUser(); - if (user.ine == null && user.birthDate == null) { - emit(UserPartialLoaded(user)); - } else { + + 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)); } } catch (e) { emit(UserError(e.toString())); @@ -42,4 +50,42 @@ class UserCubit extends Cubit { Future clearUserClass() async { await userRepository.clearClass(); } + + Future loginAndSaveId(String username, String password) async { + emit(UserLoading()); + try { + Global.setIne(username); + Global.setBirthDate(password); + final studentId = await userRepository.login(username, password); + emit(UserLoggedIn(studentId: studentId)); + } catch (e) { + emit(UserError(e.toString())); + } + } + + Future fetchMarks() async { + emit(UserLoading()); + try { + final user = await userRepository.getUser(); + if (user.studentId != null) { + final File marks = await userRepository.getMarks(user.studentId!); + final File absences = await userRepository.getAbsences(user.studentId!); + emit(UserLoaded(user)); + } else { + throw Exception('No student ID found in SharedPreferences'); + } + } catch (e) { + emit(UserError(e.toString())); + } + } + + Future checkStudentId() async { + int? id = await Global.studentId; + + if (id != null) { + emit(UserLoggedIn(studentId: id)); + } 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 f706bc9..b742cb7 100644 --- a/frontend/app_student/lib/users/cubit/user_state.dart +++ b/frontend/app_student/lib/users/cubit/user_state.dart @@ -13,16 +13,28 @@ class UserLoaded extends UserState { UserLoaded(this.user); } -class UserPartialLoaded extends UserState { +class UserNameLoaded extends UserState { final UserModel user; - UserPartialLoaded(this.user); + UserNameLoaded(this.user); } class UserClassesSelected extends UserState {} +class UserWithoutClass extends UserState { + final UserModel user; + + UserWithoutClass(this.user); +} + class UserError extends UserState { final String message; UserError(this.message); } + +class UserLoggedIn extends UserState { + final int studentId; + + UserLoggedIn({required this.studentId}); +} diff --git a/frontend/app_student/lib/utils/custom_layout.dart b/frontend/app_student/lib/utils/custom_layout.dart index ac24cf7..456281e 100644 --- a/frontend/app_student/lib/utils/custom_layout.dart +++ b/frontend/app_student/lib/utils/custom_layout.dart @@ -40,7 +40,7 @@ class CustomLayout extends StatelessWidget { Positioned( left: 0, right: 0, - bottom: 0, + bottom: 10, child: bottomContent!, ), ], diff --git a/frontend/app_student/lib/utils/global.dart b/frontend/app_student/lib/utils/global.dart index f959352..5661e67 100644 --- a/frontend/app_student/lib/utils/global.dart +++ b/frontend/app_student/lib/utils/global.dart @@ -23,4 +23,24 @@ class Global { 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); + } } From 2eb2a5682951a5bf19fdcf3a08dc5292311d951a Mon Sep 17 00:00:00 2001 From: Jules Artaud Date: Mon, 29 Apr 2024 18:34:12 +0200 Subject: [PATCH 6/7] :construction:: space school state --- .../lib/account/account_cubit.dart | 51 ---------- .../lib/account/account_state.dart | 23 ----- .../views/forms/account_link_form.dart | 69 -------------- frontend/app_student/lib/l10n/app_fr.arb | 2 +- .../app_student/lib/profil/views/profil.dart | 39 ++++---- frontend/app_student/lib/routes.dart | 13 +-- .../views/forms/link_account_form.dart | 93 +++++++++++++++++++ .../views/school_space.dart} | 54 +++++------ .../views/widgets/pdf_card.dart | 3 +- .../views/widgets/pdf_view.dart | 0 .../lib/users/cubit/user_cubit.dart | 23 +++-- .../lib/users/cubit/user_state.dart | 4 +- 12 files changed, 164 insertions(+), 210 deletions(-) delete mode 100644 frontend/app_student/lib/account/account_cubit.dart delete mode 100644 frontend/app_student/lib/account/account_state.dart delete mode 100644 frontend/app_student/lib/account/views/forms/account_link_form.dart create mode 100644 frontend/app_student/lib/school_space/views/forms/link_account_form.dart rename frontend/app_student/lib/{account/views/account.dart => school_space/views/school_space.dart} (71%) rename frontend/app_student/lib/{account => school_space}/views/widgets/pdf_card.dart (95%) rename frontend/app_student/lib/{account => school_space}/views/widgets/pdf_view.dart (100%) diff --git a/frontend/app_student/lib/account/account_cubit.dart b/frontend/app_student/lib/account/account_cubit.dart deleted file mode 100644 index 47bf8a3..0000000 --- a/frontend/app_student/lib/account/account_cubit.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:io'; -import 'package:app_student/utils/global.dart'; -import 'package:bloc/bloc.dart'; -import 'package:meta/meta.dart'; -import 'package:app_student/api/account/repositories/account_repository.dart'; - -part 'account_state.dart'; - -class AccountCubit extends Cubit { - final AccountRepository accountRepository; - - AccountCubit({required this.accountRepository}) : super(AccountInitial()); - - Future loginAndSaveId(String username, String password) async { - emit(AccountLoading()); - try { - Global.setIne(username); - Global.setBirthDate(password); - final studentId = await accountRepository.login(username, password); - emit(AccountLoggedIn(studentId: studentId)); - } catch (e) { - emit(AccountError()); - } - } - - Future fetchMarks() async { - emit(AccountLoading()); - try { - final studentId = await Global.studentId; - if (studentId != null) { - final File marks = await accountRepository.getMarks(studentId); - final File absences = await accountRepository.getAbsences(studentId); - emit(AccountLoaded(marksFile: marks, absencesFile: absences)); - } else { - throw Exception('No student ID found in SharedPreferences'); - } - } catch (e) { - emit(AccountError()); - } - } - - Future checkStudentId() async { - int? id = await Global.studentId; - - if (id != null) { - emit(AccountLoggedIn(studentId: id)); - } else { - emit(AccountInitial()); - } - } -} diff --git a/frontend/app_student/lib/account/account_state.dart b/frontend/app_student/lib/account/account_state.dart deleted file mode 100644 index dda9e8f..0000000 --- a/frontend/app_student/lib/account/account_state.dart +++ /dev/null @@ -1,23 +0,0 @@ -part of 'account_cubit.dart'; - -@immutable -abstract class AccountState {} - -class AccountInitial extends AccountState {} - -class AccountLoading extends AccountState {} - -class AccountError extends AccountState {} - -class AccountLoggedIn extends AccountState { - final int studentId; - - AccountLoggedIn({required this.studentId}); -} - -class AccountLoaded extends AccountState { - final File marksFile; - final File absencesFile; - - AccountLoaded({required this.absencesFile, required this.marksFile}); -} diff --git a/frontend/app_student/lib/account/views/forms/account_link_form.dart b/frontend/app_student/lib/account/views/forms/account_link_form.dart deleted file mode 100644 index 210f60a..0000000 --- a/frontend/app_student/lib/account/views/forms/account_link_form.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:app_student/account/account_cubit.dart'; -import 'package:app_student/shared_components/header_logo.dart'; -import 'package:app_student/users/cubit/user_cubit.dart'; -import 'package:app_student/utils/custom_layout.dart'; -import 'package:app_student/utils/global.dart'; -import 'package:flutter/material.dart'; - -class LoginFormPage extends StatelessWidget { - final UserCubit userCubit; - - const LoginFormPage({super.key, required this.userCubit}); - - @override - Widget build(BuildContext context) { - final formKey = GlobalKey(); - String ine = ''; - String birthDate = ''; - - return CustomLayout( - appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Form( - key: formKey, - child: Column( - children: [ - TextFormField( - decoration: const InputDecoration(labelText: 'INE'), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your INE'; - } - return null; - }, - onSaved: (value) { - ine = value!; - }, - ), - TextFormField( - decoration: const InputDecoration(labelText: 'Birth Date'), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your birth date'; - } - return null; - }, - onSaved: (value) { - birthDate = value!; - }, - ), - ElevatedButton( - child: const Text('Submit'), - onPressed: () { - if (formKey.currentState!.validate()) { - formKey.currentState!.save(); - userCubit.loginAndSaveId(ine, birthDate); - Navigator.of(context).pop(); - } - }, - ), - ], - ), - ), - ), - // Add your bottomBar here if you have one - // bottomBar: YourBottomBar(), - ); - } -} diff --git a/frontend/app_student/lib/l10n/app_fr.arb b/frontend/app_student/lib/l10n/app_fr.arb index 31f4c0a..79d3ef7 100644 --- a/frontend/app_student/lib/l10n/app_fr.arb +++ b/frontend/app_student/lib/l10n/app_fr.arb @@ -130,7 +130,7 @@ "@confirm": { "description": "Texte du bouton pour confirmer" }, - "my3il": "Mon 3il", + "my3il": "My3il", "@my3il": { "description": "Texte du menu" } diff --git a/frontend/app_student/lib/profil/views/profil.dart b/frontend/app_student/lib/profil/views/profil.dart index 636905c..55b3779 100644 --- a/frontend/app_student/lib/profil/views/profil.dart +++ b/frontend/app_student/lib/profil/views/profil.dart @@ -25,9 +25,8 @@ class ProfilPage extends StatelessWidget { appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), body: BlocBuilder( builder: (context, state) { - - if (state is UserLoading) { - return const Center(child: CircularProgressIndicator()); + if (state is UserLoading){ + return Container(); } else if (state is UserNameLoaded) { final user = state.user; return Column( @@ -80,20 +79,28 @@ class ProfilPage extends StatelessWidget { } }, ), - bottomContent: CustomButton( - text: AppLocalizations.of(context)!.disconnect, - onPressed: () async { - final goRouter = GoRouter.of(context); - await context.read().deleteUser(); - goRouter.go(AppRoutes.loginPage); - WidgetsBinding.instance.addPostFrameCallback((_) { - Fluttertoast.showToast( - msg: AppLocalizations.of(context)!.disconnectedMessage, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - textColor: Colors.white, + bottomContent: BlocBuilder( + builder: (context, state) { + if (state is UserLoading) { + return const Center(child: CircularProgressIndicator()); + } else { + return CustomButton( + text: AppLocalizations.of(context)!.disconnect, + onPressed: () async { + final goRouter = GoRouter.of(context); + await context.read().deleteUser(); + goRouter.go(AppRoutes.loginPage); + WidgetsBinding.instance.addPostFrameCallback((_) { + Fluttertoast.showToast( + msg: AppLocalizations.of(context)!.disconnectedMessage, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + textColor: Colors.white, + ); + }); + }, ); - }); + } }, ), bottomBar: const MenuBarView(), diff --git a/frontend/app_student/lib/routes.dart b/frontend/app_student/lib/routes.dart index 6197b5a..fb3298d 100644 --- a/frontend/app_student/lib/routes.dart +++ b/frontend/app_student/lib/routes.dart @@ -1,5 +1,4 @@ // lib/routes.dart -import 'package:app_student/account/views/account.dart'; import 'package:app_student/api/api_service.dart'; import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; import 'package:app_student/api/users/repositories/user_repository.dart'; @@ -7,15 +6,13 @@ 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.dart'; +import 'package:app_student/school_space/views/school_space.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'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; - -import 'account/account_cubit.dart'; -import 'api/account/repositories/account_repository.dart'; import 'class_groups/views/class_group.dart'; import 'login/cubit/login_cubit.dart'; import 'login/views/login.dart'; @@ -150,18 +147,18 @@ class AppRoutes { child: MultiRepositoryProvider( providers: [ RepositoryProvider( - create: (context) => AccountRepository( + create: (context) => UserRepository( apiService: ApiService(apiUrl: context.read().apiUrl))), ], child: MultiBlocProvider( providers: [ BlocProvider( - create: (context) => AccountCubit( - accountRepository: context.read()), + create: (context) => UserCubit( + userRepository: context.read()), ), ], - child: const AccountPage(), + child: const SchoolSpacePage(), ), ), ), 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 new file mode 100644 index 0000000..cbb8ada --- /dev/null +++ b/frontend/app_student/lib/school_space/views/forms/link_account_form.dart @@ -0,0 +1,93 @@ +import 'package:app_student/shared_components/header_logo.dart'; +import 'package:app_student/shared_components/header_title.dart'; +import 'package:app_student/users/cubit/user_cubit.dart'; +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:intl/intl.dart'; + +class LinkAccountForm extends StatelessWidget { + final UserCubit userCubit; + + const LinkAccountForm({super.key, required this.userCubit}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + String ine = ''; + String birthDate = ''; + final birthDateController = TextEditingController(); + + return CustomLayout( + appBar: HeaderLogo(appBarHeight: Global.screenHeight * 0.3), + body: Column( + children: [ + const HeaderTitle('Connexion'), + Form( + key: formKey, + child: Column( + children: [ + TextFormField( + decoration: const InputDecoration(labelText: 'INE'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your INE'; + } + return null; + }, + onSaved: (value) { + ine = value!; + }, + ), + TextFormField( + decoration: InputDecoration( + labelText: 'Birth Date', + suffixIcon: IconButton( + icon: const Icon(Icons.calendar_today), + onPressed: () async { + final DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(1900), + lastDate: DateTime.now(), + ); + if (pickedDate != null) { + birthDate = DateFormat('dd/MM/yyyy').format(pickedDate); + birthDateController.text = birthDate; + formKey.currentState!.validate(); + } + }, + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your birth date'; + } + return null; + }, + onSaved: (value) { + birthDate = value!; + }, + controller: birthDateController, + ), + ], + ), + ), + ], + ), + bottomContent: CustomButton( + text: 'Valider', + onPressed: () { + if (formKey.currentState!.validate()) { + formKey.currentState!.save(); + userCubit.loginAndSaveId(ine, birthDate); + Navigator.of(context).pop(); + } + }, + ), + // Add your bottomBar here if you have one + // bottomBar: YourBottomBar(), + ); + } +} diff --git a/frontend/app_student/lib/account/views/account.dart b/frontend/app_student/lib/school_space/views/school_space.dart similarity index 71% rename from frontend/app_student/lib/account/views/account.dart rename to frontend/app_student/lib/school_space/views/school_space.dart index 007f82b..bfb4da3 100644 --- a/frontend/app_student/lib/account/views/account.dart +++ b/frontend/app_student/lib/school_space/views/school_space.dart @@ -1,6 +1,5 @@ -import 'package:app_student/account/views/forms/account_link_form.dart'; -import 'package:app_student/account/views/widgets/pdf_card.dart'; import 'package:app_student/users/cubit/user_cubit.dart'; +import 'package:app_student/utils/custom_button.dart'; import 'package:app_student/utils/custom_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -11,29 +10,23 @@ import '../../shared_components/header_logo.dart'; import '../../shared_components/header_title.dart'; import '../../utils/custom_layout.dart'; import '../../utils/global.dart'; -import '../account_cubit.dart'; +import 'forms/link_account_form.dart'; +import 'widgets/pdf_card.dart'; -class AccountPage extends StatefulWidget { - const AccountPage({super.key}); +class SchoolSpacePage extends StatefulWidget { + const SchoolSpacePage({super.key}); @override - AccountPageState createState() => AccountPageState(); + SchoolSpacePageState createState() => SchoolSpacePageState(); } -class AccountPageState extends State { +class SchoolSpacePageState extends State { late final UserCubit userCubit; @override void initState() { super.initState(); - userCubit = BlocProvider.of(context); - userCubit.checkStudentId(); - - userCubit.stream.listen((state) { - if (state is AccountLoggedIn) { - userCubit.fetchMarks(); - } - }); + userCubit = context.read(); } @override @@ -44,7 +37,7 @@ class AccountPageState extends State { builder: (context, state) { if (state is UserLoading) { return const Center(child: CircularProgressIndicator()); - } else if (state is UserLoaded) { + } else if (state is UserLoggedIn) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -65,7 +58,8 @@ class AccountPageState extends State { ), const SizedBox(height: 40), PdfCard( - filePath: state.user.marksFile!.path, title: 'Relevé de notes'), + filePath: state.user.marksFile!.path, + title: 'Relevé de notes'), const SizedBox(height: 20), PdfCard( filePath: state.user.absencesFile!.path, @@ -93,20 +87,18 @@ class AccountPageState extends State { ), ), ), - const SizedBox(height: 20), - Center( - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - LoginFormPage(userCubit: userCubit), - ), - ); - }, - child: const Text('Relier mon compte'), - ), + SizedBox(height: Global.screenHeight * 0.2), + CustomButton( + text: 'Relier mon compte', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + LinkAccountForm(userCubit: userCubit), + ), + ); + }, ), ], ); diff --git a/frontend/app_student/lib/account/views/widgets/pdf_card.dart b/frontend/app_student/lib/school_space/views/widgets/pdf_card.dart similarity index 95% rename from frontend/app_student/lib/account/views/widgets/pdf_card.dart rename to frontend/app_student/lib/school_space/views/widgets/pdf_card.dart index 0586742..6e5689d 100644 --- a/frontend/app_student/lib/account/views/widgets/pdf_card.dart +++ b/frontend/app_student/lib/school_space/views/widgets/pdf_card.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:app_student/account/views/widgets/pdf_view.dart'; import 'package:app_student/utils/custom_theme.dart'; +import 'pdf_view.dart'; + class PdfCard extends StatelessWidget { final String filePath; final String title; diff --git a/frontend/app_student/lib/account/views/widgets/pdf_view.dart b/frontend/app_student/lib/school_space/views/widgets/pdf_view.dart similarity index 100% rename from frontend/app_student/lib/account/views/widgets/pdf_view.dart rename to frontend/app_student/lib/school_space/views/widgets/pdf_view.dart diff --git a/frontend/app_student/lib/users/cubit/user_cubit.dart b/frontend/app_student/lib/users/cubit/user_cubit.dart index 7700485..38f85ae 100644 --- a/frontend/app_student/lib/users/cubit/user_cubit.dart +++ b/frontend/app_student/lib/users/cubit/user_cubit.dart @@ -22,15 +22,20 @@ class UserCubit extends Cubit { if (user.className == null || user.className!.isEmpty) { emit(UserWithoutClass(user)); - } else if (user.ine != null && user.ine!.isNotEmpty && + } 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) { + } else if (user.ine == null || + (user.ine != null && user.ine!.isEmpty) || + user.birthDate == null) { emit(UserNameLoaded(user)); } } catch (e) { - emit(UserError(e.toString())); + if (!isClosed) { + emit(UserError(e.toString())); + } } } @@ -54,10 +59,12 @@ class UserCubit extends Cubit { Future loginAndSaveId(String username, String password) async { emit(UserLoading()); try { + final studentId = await userRepository.login(username, password); Global.setIne(username); Global.setBirthDate(password); - final studentId = await userRepository.login(username, password); - emit(UserLoggedIn(studentId: studentId)); + Global.setStudentId(studentId); + final user = await userRepository.getUser(); + emit(UserLoggedIn(user)); } catch (e) { emit(UserError(e.toString())); } @@ -80,10 +87,10 @@ class UserCubit extends Cubit { } Future checkStudentId() async { - int? id = await Global.studentId; + final user = await getCurrentUser(); - if (id != null) { - emit(UserLoggedIn(studentId: id)); + 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 b742cb7..905c622 100644 --- a/frontend/app_student/lib/users/cubit/user_state.dart +++ b/frontend/app_student/lib/users/cubit/user_state.dart @@ -34,7 +34,7 @@ class UserError extends UserState { } class UserLoggedIn extends UserState { - final int studentId; + final UserModel user; - UserLoggedIn({required this.studentId}); + UserLoggedIn(this.user); } From 68e4f0da51c9f5931b8e13f7290ae7b6fdb2dab3 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 21 May 2024 11:32:35 +0200 Subject: [PATCH 7/7] Merge remote-tracking branch 'origin/dev' into feat/marks-absences --- frontend/app_student/lib/users/cubit/user_cubit.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/app_student/lib/users/cubit/user_cubit.dart b/frontend/app_student/lib/users/cubit/user_cubit.dart index 38f85ae..d1f3c30 100644 --- a/frontend/app_student/lib/users/cubit/user_cubit.dart +++ b/frontend/app_student/lib/users/cubit/user_cubit.dart @@ -3,7 +3,7 @@ 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'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart'; import '../../api/users/repositories/user_repository.dart'; import '../../utils/global.dart'; @@ -76,7 +76,13 @@ class UserCubit extends Cubit { 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');