diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..8f00030 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/.idea/3il-student.iml b/.idea/3il-student.iml new file mode 100644 index 0000000..88725d4 --- /dev/null +++ b/.idea/3il-student.iml @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..d8e9561 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml new file mode 100644 index 0000000..b0594fc --- /dev/null +++ b/.idea/libraries/Dart_Packages.xml @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 0000000..79bdae7 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6e86672 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6ee7f41 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..01ea055 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. + // Pointez pour afficher la description des attributs existants. + // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "app_student", + "cwd": "frontend\\app_student", + "request": "launch", + "type": "dart" + }, + { + "name": "app_student (profile mode)", + "cwd": "frontend\\app_student", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "app_student (release mode)", + "cwd": "frontend\\app_student", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/backend/composer.json b/backend/composer.json index 8410887..cd9945d 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -21,6 +21,7 @@ "symfony/yaml": "6.4.*" }, "require-dev": { + "symfony/debug-bundle": "6.4.*", "symfony/maker-bundle": "^1.53" }, "config": { diff --git a/backend/composer.lock b/backend/composer.lock index 6678a68..216579f 100644 --- a/backend/composer.lock +++ b/backend/composer.lock @@ -4,7 +4,9 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e478085c90ffbf3ef8d4b96ac02a834c", + + "content-hash": "30f9eda7517c76d9b97fe895a2ce5b36", + "packages": [ { "name": "masterminds/html5", @@ -3023,6 +3025,80 @@ }, "time": "2024-02-21T19:24:10+00:00" }, + { + "name": "symfony/debug-bundle", + "version": "v6.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug-bundle.git", + "reference": "425c7760a4e6fdc6cb643c791d32277037c971df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/425c7760a4e6fdc6cb643c791d32277037c971df", + "reference": "425c7760a4e6fdc6cb643c791d32277037c971df", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": ">=8.1", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/twig-bridge": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/dependency-injection": "<5.4" + }, + "require-dev": { + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/web-profiler-bundle": "^5.4|^6.0|^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\DebugBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug-bundle/tree/v6.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T14:51:35+00:00" + }, { "name": "symfony/maker-bundle", "version": "v1.56.0", @@ -3175,6 +3251,265 @@ } ], "time": "2024-02-20T12:31:00+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "06450585bf65e978026bda220cdebca3f867fde7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", + "reference": "06450585bf65e978026bda220cdebca3f867fde7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-26T14:02:43+00:00" + }, + { + "name": "symfony/twig-bridge", + "version": "v6.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "256f330026d1c97187b61aa5c29e529499877f13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/256f330026d1c97187b61aa5c29e529499877f13", + "reference": "256f330026d1c97187b61aa5c29e529499877f13", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<5.4", + "symfony/form": "<6.3", + "symfony/http-foundation": "<5.4", + "symfony/http-kernel": "<6.4", + "symfony/mime": "<6.2", + "symfony/serializer": "<6.4", + "symfony/translation": "<5.4", + "symfony/workflow": "<5.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/asset-mapper": "^6.3|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.1|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^5.4|^6.0|^7.0", + "symfony/security-csrf": "^5.4|^6.0|^7.0", + "symfony/security-http": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^6.1|^7.0", + "symfony/web-link": "^5.4|^6.0|^7.0", + "symfony/workflow": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v6.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-15T11:26:02+00:00" + }, + { + "name": "twig/twig", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T18:54:41+00:00" } ], "aliases": [], diff --git a/backend/config/bundles.php b/backend/config/bundles.php index ffeb861..2e25f6e 100644 --- a/backend/config/bundles.php +++ b/backend/config/bundles.php @@ -3,4 +3,5 @@ return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], ]; diff --git a/backend/config/packages/debug.yaml b/backend/config/packages/debug.yaml new file mode 100644 index 0000000..ad874af --- /dev/null +++ b/backend/config/packages/debug.yaml @@ -0,0 +1,5 @@ +when@dev: + debug: + # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. + # See the "server:dump" command to start a new server. + dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" diff --git a/backend/symfony.lock b/backend/symfony.lock index 71d8362..72966a0 100644 --- a/backend/symfony.lock +++ b/backend/symfony.lock @@ -23,6 +23,18 @@ "./bin/console" ] }, + "symfony/debug-bundle": { + "version": "6.4", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.3", + "ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b" + }, + "files": [ + "./config/packages/debug.yaml" + ] + }, "symfony/flex": { "version": "2.4", "recipe": { diff --git a/frontend/app_student/.run/main_dev.run.xml b/frontend/app_student/.run/main_dev.run.xml new file mode 100644 index 0000000..b98f49e --- /dev/null +++ b/frontend/app_student/.run/main_dev.run.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/app_student/.run/main_prod.run.xml b/frontend/app_student/.run/main_prod.run.xml new file mode 100644 index 0000000..7a31433 --- /dev/null +++ b/frontend/app_student/.run/main_prod.run.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/app_student/lib/api/api_service.dart b/frontend/app_student/lib/api/api_service.dart new file mode 100644 index 0000000..531d406 --- /dev/null +++ b/frontend/app_student/lib/api/api_service.dart @@ -0,0 +1,24 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +class ApiService { + final String apiUrl; + + ApiService({required this.apiUrl}); + + Future> getData( + String endpoint, T Function(Map) fromJson) async { + try { + final response = await http.get(Uri.parse('$apiUrl$endpoint')); + + if (response.statusCode == 200) { + List jsonResponse = json.decode(response.body); + return jsonResponse.map((item) => fromJson(item)).toList(); + } else { + throw Exception('Failed to load data'); + } + } catch (e) { + throw Exception('Failed to load data: $e'); + } + } +} diff --git a/frontend/app_student/lib/api/classes/entities/class.dart b/frontend/app_student/lib/api/classes/entities/class.dart new file mode 100644 index 0000000..4f3e2b1 --- /dev/null +++ b/frontend/app_student/lib/api/classes/entities/class.dart @@ -0,0 +1,13 @@ +class Class { + final String file; + final String name; + + Class({required this.file, required this.name}); + + factory Class.fromJson(Map json) { + return Class( + file: json['file'], + name: json['name'], + ); + } +} diff --git a/frontend/app_student/lib/api/classes/repositories/class_repository.dart b/frontend/app_student/lib/api/classes/repositories/class_repository.dart new file mode 100644 index 0000000..d37d6cf --- /dev/null +++ b/frontend/app_student/lib/api/classes/repositories/class_repository.dart @@ -0,0 +1,12 @@ +import 'package:app_student/api/classes/entities/class.dart'; +import 'package:app_student/api/api_service.dart'; + +class ClassRepository { + final ApiService apiService; + + ClassRepository({required this.apiService}); + + Future> getClasses() { + return apiService.getData('/api/classes', (item) => Class.fromJson(item)); + } +} diff --git a/frontend/app_student/lib/api/courses/entities/course.dart b/frontend/app_student/lib/api/courses/entities/course.dart new file mode 100644 index 0000000..93655dc --- /dev/null +++ b/frontend/app_student/lib/api/courses/entities/course.dart @@ -0,0 +1,31 @@ +class Course { + final String creneau; + final String activite; + final String id; + final String couleur; + final Map horaire; + final String salle; + + Course({ + required this.creneau, + required this.activite, + required this.id, + required this.couleur, + required this.horaire, + required this.salle, + }); + + factory Course.fromJson(Map json) { + return Course( + creneau: json['creneau'] ?? 'null', + activite: json['activite'] ?? 'null', + id: json['id'] ?? 'null', + couleur: json['couleur'] ?? 'null', + horaire: { + 'start': json['horaire']['start'] ?? 'null', + 'end': json['horaire']['end'] ?? 'null', + }, + salle: json['salle'] ?? 'null', + ); + } +} diff --git a/frontend/app_student/lib/api/day_schedule/entities/day_schedule.dart b/frontend/app_student/lib/api/day_schedule/entities/day_schedule.dart new file mode 100644 index 0000000..77c70a2 --- /dev/null +++ b/frontend/app_student/lib/api/day_schedule/entities/day_schedule.dart @@ -0,0 +1,25 @@ +import '../../courses/entities/course.dart'; + +class DaySchedule { + final String date; + final String jour; + final List cours; + + DaySchedule({ + required this.date, + required this.jour, + required this.cours, + }); + + factory DaySchedule.fromJson(Map json) { + var coursFromJson = json['cours'] as List; + List coursList = + coursFromJson.map((i) => Course.fromJson(i)).toList(); + + return DaySchedule( + date: json['date'], + jour: json['jour'], + cours: coursList, + ); + } +} diff --git a/frontend/app_student/lib/api/day_schedule/repositories/day_schedule.dart b/frontend/app_student/lib/api/day_schedule/repositories/day_schedule.dart new file mode 100644 index 0000000..c0fa80a --- /dev/null +++ b/frontend/app_student/lib/api/day_schedule/repositories/day_schedule.dart @@ -0,0 +1,14 @@ +import 'package:app_student/api/api_service.dart'; +import '../entities/day_schedule.dart'; + +class DayScheduleRepository { + final String className; + final ApiService apiService; + + DayScheduleRepository({required this.className, required this.apiService}); + + Future> getDaySchedules($className) { + return apiService.getData('/api/timetable?class_param=$className', + (item) => DaySchedule.fromJson(item)); + } +} diff --git a/frontend/app_student/lib/classes/bloc/class_bloc.dart b/frontend/app_student/lib/classes/bloc/class_bloc.dart new file mode 100644 index 0000000..8fdcab1 --- /dev/null +++ b/frontend/app_student/lib/classes/bloc/class_bloc.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:app_student/api/classes/entities/class.dart'; +import 'package:app_student/api/classes/repositories/class_repository.dart'; + + + +class ClassBloc { + final ClassRepository classRepository; + final _classController = StreamController>(); + + Stream> get classStream => _classController.stream; + + ClassBloc({required this.classRepository}) { + fetchClasses(); + } + + fetchClasses() async { + final classes = await classRepository.getClasses(); + _classController.sink.add(classes); + } + + dispose() { + _classController.close(); + } +} diff --git a/frontend/app_student/lib/classes/views/class.dart b/frontend/app_student/lib/classes/views/class.dart new file mode 100644 index 0000000..465b331 --- /dev/null +++ b/frontend/app_student/lib/classes/views/class.dart @@ -0,0 +1,60 @@ +import 'package:app_student/api/classes/entities/class.dart'; +import 'package:flutter/material.dart'; +import 'package:app_student/classes/bloc/class_bloc.dart'; + + +class ClassListPage extends StatefulWidget { + final ClassBloc classBloc; + + const ClassListPage({required this.classBloc, super.key}); + + @override + ClassListPageState createState() => ClassListPageState(); +} + +class ClassListPageState extends State { + late ClassBloc _classBloc; + + @override + void initState() { + super.initState(); + _classBloc = widget.classBloc; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Class List'), + ), + body: StreamBuilder>( + stream: _classBloc.classStream, + builder: (BuildContext context, AsyncSnapshot> snapshot) { + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(snapshot.data![index].name), + subtitle: Text(snapshot.data![index].file), + ); + }, + ); + }, + ), + ); + } + + @override + void dispose() { + _classBloc.dispose(); + super.dispose(); + } +} diff --git a/frontend/app_student/lib/config/config.dart b/frontend/app_student/lib/config/config.dart new file mode 100644 index 0000000..ef552f2 --- /dev/null +++ b/frontend/app_student/lib/config/config.dart @@ -0,0 +1,3 @@ +abstract class Config { + String get apiUrl; +} diff --git a/frontend/app_student/lib/config/dev_config.dart b/frontend/app_student/lib/config/dev_config.dart new file mode 100644 index 0000000..af2b236 --- /dev/null +++ b/frontend/app_student/lib/config/dev_config.dart @@ -0,0 +1,6 @@ +import 'config.dart'; + +class DevConfig extends Config { + @override + String get apiUrl => 'https://api-dev.lukasvalois.com'; +} diff --git a/frontend/app_student/lib/config/prod_config.dart b/frontend/app_student/lib/config/prod_config.dart new file mode 100644 index 0000000..ae7a73c --- /dev/null +++ b/frontend/app_student/lib/config/prod_config.dart @@ -0,0 +1,8 @@ +import 'config.dart'; + +class ProdConfig extends Config { + @override + // TODO: Implement the new production API URL + String get apiUrl => 'https://api-dev.lukasvalois.com'; + +} diff --git a/frontend/app_student/lib/day_schedules/bloc/day_schedule_bloc.dart b/frontend/app_student/lib/day_schedules/bloc/day_schedule_bloc.dart new file mode 100644 index 0000000..40eb95d --- /dev/null +++ b/frontend/app_student/lib/day_schedules/bloc/day_schedule_bloc.dart @@ -0,0 +1,25 @@ +import 'dart:async'; +import 'package:app_student/api/day_schedule/entities/day_schedule.dart'; +import 'package:app_student/api/day_schedule/repositories/day_schedule.dart'; + +class DayScheduleBloc { + final DayScheduleRepository dayScheduleRepository; + final _dayScheduleController = StreamController>(); + + Stream> get dayScheduleStream => + _dayScheduleController.stream; + + DayScheduleBloc({required this.dayScheduleRepository}) { + fetchDaySchedules('B3 GROUPE 3 DLW-FA'); + // TODO : get the class name from the shared preferences + } + + fetchDaySchedules(String className) async { + final daySchedules = await dayScheduleRepository.getDaySchedules(className); + _dayScheduleController.sink.add(daySchedules); + } + + dispose() { + _dayScheduleController.close(); + } +} diff --git a/frontend/app_student/lib/day_schedules/views/day_schedule.dart b/frontend/app_student/lib/day_schedules/views/day_schedule.dart new file mode 100644 index 0000000..a522a09 --- /dev/null +++ b/frontend/app_student/lib/day_schedules/views/day_schedule.dart @@ -0,0 +1,36 @@ +import 'package:app_student/day_schedules/bloc/day_schedule_bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:app_student/api/day_schedule/entities/day_schedule.dart'; + +class DayScheduleView extends StatelessWidget { + final DayScheduleBloc dayScheduleBloc; + + const DayScheduleView({super.key, required this.dayScheduleBloc}); + + @override + Widget build(BuildContext context) { + return StreamBuilder>( + stream: dayScheduleBloc.dayScheduleStream, + builder: (context, snapshot) { + if (snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + final daySchedule = snapshot.data![index]; + return Card( + child: ListTile( + title: Text(daySchedule.date), + subtitle: Text('Jour: ${daySchedule.jour}'), + ), + ); + }, + ); + } else if (snapshot.hasError) { + return Text('Erreur: ${snapshot.error}'); + } + + return const Text('Erreur: Les données ne sont pas encore disponibles'); + }, + ); + } +} diff --git a/frontend/app_student/lib/main.dart b/frontend/app_student/lib/main.dart deleted file mode 100644 index 8e94089..0000000 --- a/frontend/app_student/lib/main.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart new file mode 100644 index 0000000..0ba4f9a --- /dev/null +++ b/frontend/app_student/lib/main_dev.dart @@ -0,0 +1,39 @@ +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/classes/repositories/class_repository.dart'; +import 'package:app_student/classes/views/class.dart'; +import 'package:app_student/config/dev_config.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'classes/bloc/class_bloc.dart'; +import 'config/config.dart'; + +void main() { + runApp( + Provider( + create: (_) => DevConfig(), + child: const MyApp(), + ), + ); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + final config = Provider.of(context); + final ApiService apiService = ApiService(apiUrl: config.apiUrl); + final classBloc = + ClassBloc(classRepository: ClassRepository(apiService: apiService)); + + return MaterialApp( + title: 'Class List', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: ClassListPage( + classBloc: classBloc, + ), + ); + } +} diff --git a/frontend/app_student/lib/main_prod.dart b/frontend/app_student/lib/main_prod.dart new file mode 100644 index 0000000..c0adc91 --- /dev/null +++ b/frontend/app_student/lib/main_prod.dart @@ -0,0 +1,42 @@ +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/day_schedule/repositories/day_schedule.dart'; +import 'package:app_student/config/prod_config.dart'; +import 'package:app_student/day_schedules/bloc/day_schedule_bloc.dart'; +import 'package:app_student/day_schedules/views/day_schedule.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'config/config.dart'; + +void main() { + runApp( + Provider( + create: (_) => ProdConfig(), + child: const MyApp(), + ), + ); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + final config = Provider.of(context); + final ApiService apiService = ApiService(apiUrl: config.apiUrl); + + final dayScheduleBloc = DayScheduleBloc( + dayScheduleRepository: DayScheduleRepository( + apiService: apiService, className: 'B3%20Groupe%203%20DLW-FA')); + + + return MaterialApp( + title: 'Class List', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: DayScheduleView( + dayScheduleBloc: dayScheduleBloc, + ), + ); + } +} diff --git a/frontend/app_student/pubspec.lock b/frontend/app_student/pubspec.lock index f5b0af9..b5fb585 100644 --- a/frontend/app_student/pubspec.lock +++ b/frontend/app_student/pubspec.lock @@ -75,6 +75,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" leak_tracker: dependency: transitive description: @@ -131,6 +147,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -139,6 +163,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter @@ -192,6 +224,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" vector_math: dependency: transitive description: @@ -208,5 +248,14 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" sdks: dart: ">=3.3.0 <4.0.0" + flutter: ">=1.16.0" diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index 2a36cc0..9d54152 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -35,6 +35,8 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 + http: ^1.2.1 + provider: ^6.1.2 dev_dependencies: flutter_test: diff --git a/frontend/app_student/test/widget_test.dart b/frontend/app_student/test/widget_test.dart index da445b1..ef5fd51 100644 --- a/frontend/app_student/test/widget_test.dart +++ b/frontend/app_student/test/widget_test.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:app_student/main.dart'; +import 'package:app_student/main_dev.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async {