From 3f884fc5f1b5de67a8917c026d34ee212ae70dc2 Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:01:15 +0100 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=9A=A7:=20handle=20sharedpreference?= =?UTF-8?q?s=20when=20login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/app_student/lib/login/cubit/login_cubit.dart | 8 ++++++++ frontend/app_student/lib/login/cubit/login_state.dart | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 frontend/app_student/lib/login/cubit/login_cubit.dart create mode 100644 frontend/app_student/lib/login/cubit/login_state.dart diff --git a/frontend/app_student/lib/login/cubit/login_cubit.dart b/frontend/app_student/lib/login/cubit/login_cubit.dart new file mode 100644 index 0000000..b394666 --- /dev/null +++ b/frontend/app_student/lib/login/cubit/login_cubit.dart @@ -0,0 +1,8 @@ +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +part 'login_state.dart'; + +class LoginCubit extends Cubit { + LoginCubit() : super(LoginInitial()); +} diff --git a/frontend/app_student/lib/login/cubit/login_state.dart b/frontend/app_student/lib/login/cubit/login_state.dart new file mode 100644 index 0000000..9dd6480 --- /dev/null +++ b/frontend/app_student/lib/login/cubit/login_state.dart @@ -0,0 +1,6 @@ +part of 'login_cubit.dart'; + +@immutable +abstract class LoginState {} + +class LoginInitial extends LoginState {} From 6632477e6dea3b8a538762f952b5818f38608a54 Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:31:58 +0100 Subject: [PATCH 02/10] =?UTF-8?q?=E2=9E=95:=20added=20shared=20preferences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/app_student/pubspec.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index f606863..257dbc8 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -37,6 +37,8 @@ dependencies: cupertino_icons: ^1.0.6 http: ^1.2.1 provider: ^6.1.2 + bloc: ^8.1.3 + shared_preferences: ^2.2.2 dev_dependencies: flutter_test: From 424b449b811a156512e291e9c3a3ce242de9ddfc Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:32:58 +0100 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=94=A7:=20added=20devtools=20option?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/app_student/devtools_options.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 frontend/app_student/devtools_options.yaml diff --git a/frontend/app_student/devtools_options.yaml b/frontend/app_student/devtools_options.yaml new file mode 100644 index 0000000..7e7e7f6 --- /dev/null +++ b/frontend/app_student/devtools_options.yaml @@ -0,0 +1 @@ +extensions: From 76f335ee1897b05c04e47c2213715bd0d9cc1262 Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:34:43 +0100 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=92=A9:=20je=20test=20la?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-backend-dev.yml | 57 +++++ .github/workflows/execute-linter-front.yml | 44 ++++ backend/.php-cs-fixer.cache | 1 + backend/.php-cs-fixer.dist.php | 13 ++ backend/compose.override.yaml | 8 + backend/compose.yaml | 21 ++ backend/config/packages/doctrine.yaml | 50 +++++ .../config/packages/doctrine_migrations.yaml | 6 + backend/migrations/.gitignore | 0 backend/openapi.json | 194 ++++++++++++++++++ backend/src/Entity/ClassGroups.php | 50 +++++ backend/src/Entity/DaySchedule.php | 114 ++++++++++ backend/src/Entity/Event.php | 134 ++++++++++++ backend/src/Entity/EventHours.php | 55 +++++ backend/src/Entity/WeekSchedule.php | 82 ++++++++ backend/src/Repository/.gitignore | 0 .../src/Repository/ClassGroupsRepository.php | 48 +++++ .../src/Repository/DayScheduleRepository.php | 48 +++++ .../src/Repository/EventHoursRepository.php | 48 +++++ backend/src/Repository/EventRepository.php | 48 +++++ .../src/Repository/WeekScheduleRepository.php | 48 +++++ backend/src/Service/JsonService.php | 35 ++++ backend/src/Service/XMLService.php | 41 ++++ composer.json | 5 + composer.lock | 193 +++++++++++++++++ .../entities/class_group_entity.dart | 13 ++ .../models/class_group_model.dart | 10 + .../repositories/class_group_repository.dart | 16 ++ .../entities/day_schedule_entity.dart | 27 +++ .../models/day_schedule_model.dart | 14 ++ .../entities/event_hours_entity.dart | 16 ++ .../event_hours/models/event_hours_model.dart | 10 + .../lib/api/events/entities/event_entity.dart | 33 +++ .../lib/api/events/models/event_model.dart | 15 ++ .../lib/api/users/entities/user_entity.dart | 14 ++ .../lib/api/users/models/user_model.dart | 11 + .../entities/week_schedule_entity.dart | 21 ++ .../models/week_schedule_model.dart | 14 ++ .../week_schedule_repositories.dart | 16 ++ .../class_groups/cubit/class_group_cubit.dart | 22 ++ .../class_groups/cubit/class_group_state.dart | 20 ++ .../class_groups/views/class_group_view.dart | 45 ++++ .../cubit/week_schedule_cubit.dart | 23 +++ .../cubit/week_schedule_state.dart | 20 ++ .../week_schedule/views/week_schedule.dart | 83 ++++++++ 45 files changed, 1786 insertions(+) create mode 100644 .github/workflows/deploy-backend-dev.yml create mode 100644 .github/workflows/execute-linter-front.yml create mode 100644 backend/.php-cs-fixer.cache create mode 100644 backend/.php-cs-fixer.dist.php create mode 100644 backend/compose.override.yaml create mode 100644 backend/compose.yaml create mode 100644 backend/config/packages/doctrine.yaml create mode 100644 backend/config/packages/doctrine_migrations.yaml create mode 100644 backend/migrations/.gitignore create mode 100644 backend/openapi.json create mode 100644 backend/src/Entity/ClassGroups.php create mode 100644 backend/src/Entity/DaySchedule.php create mode 100644 backend/src/Entity/Event.php create mode 100644 backend/src/Entity/EventHours.php create mode 100644 backend/src/Entity/WeekSchedule.php create mode 100644 backend/src/Repository/.gitignore create mode 100644 backend/src/Repository/ClassGroupsRepository.php create mode 100644 backend/src/Repository/DayScheduleRepository.php create mode 100644 backend/src/Repository/EventHoursRepository.php create mode 100644 backend/src/Repository/EventRepository.php create mode 100644 backend/src/Repository/WeekScheduleRepository.php create mode 100644 backend/src/Service/JsonService.php create mode 100644 backend/src/Service/XMLService.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 frontend/app_student/lib/api/class_groups/entities/class_group_entity.dart create mode 100644 frontend/app_student/lib/api/class_groups/models/class_group_model.dart create mode 100644 frontend/app_student/lib/api/class_groups/repositories/class_group_repository.dart create mode 100644 frontend/app_student/lib/api/day_schedule/entities/day_schedule_entity.dart create mode 100644 frontend/app_student/lib/api/day_schedule/models/day_schedule_model.dart create mode 100644 frontend/app_student/lib/api/event_hours/entities/event_hours_entity.dart create mode 100644 frontend/app_student/lib/api/event_hours/models/event_hours_model.dart create mode 100644 frontend/app_student/lib/api/events/entities/event_entity.dart create mode 100644 frontend/app_student/lib/api/events/models/event_model.dart create mode 100644 frontend/app_student/lib/api/users/entities/user_entity.dart create mode 100644 frontend/app_student/lib/api/users/models/user_model.dart create mode 100644 frontend/app_student/lib/api/week_schedule/entities/week_schedule_entity.dart create mode 100644 frontend/app_student/lib/api/week_schedule/models/week_schedule_model.dart create mode 100644 frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repositories.dart create mode 100644 frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart create mode 100644 frontend/app_student/lib/class_groups/cubit/class_group_state.dart create mode 100644 frontend/app_student/lib/class_groups/views/class_group_view.dart create mode 100644 frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart create mode 100644 frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart create mode 100644 frontend/app_student/lib/week_schedule/views/week_schedule.dart diff --git a/.github/workflows/deploy-backend-dev.yml b/.github/workflows/deploy-backend-dev.yml new file mode 100644 index 0000000..31008fb --- /dev/null +++ b/.github/workflows/deploy-backend-dev.yml @@ -0,0 +1,57 @@ +name: Upload backend to dev server and lint + +on: + push: + branches: + - dev + pull_request: + branches: + - dev + +env: + FLUTTER_VERSION: 3.10.1 + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install SSH key + uses: webfactory/ssh-agent@v0.5.3 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Upload PHP code + run: scp -o StrictHostKeyChecking=no -r * ${{ secrets.SERVER_USERNAME }}@${{ secrets.SERVER_HOST }}:/home/julesartd/www/api-dev-edt-3il + + + install_dependencies: + needs: deploy + runs-on: ubuntu-latest + + steps: + - name: SSH Into Server and Install Composer Dependencies + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + cd /home/julesartd/www/api-dev-edt-3il/backend + composer2 install + + - name: SSH Into Server and execute Linter for PHP + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + cd /home/julesartd/www/api-dev-edt-3il/backend + vendor/bin/php-cs-fixer fix src + + + \ No newline at end of file diff --git a/.github/workflows/execute-linter-front.yml b/.github/workflows/execute-linter-front.yml new file mode 100644 index 0000000..8f51356 --- /dev/null +++ b/.github/workflows/execute-linter-front.yml @@ -0,0 +1,44 @@ +name: Execute Lint and Format + +concurrency: + group: $-$ + cancel-in-progress: true + +on: + push: + branches: + - main + - dev + pull_request: + branches: + - main + - dev + +env: + FLUTTER_VERSION: 3.19.2 + +jobs: + format-lint: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + cache: true + cache-key: ":os:-modules-:hash:" + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Install dependencies + run: | + cd frontend/app_student + flutter pub get + + - name: Formatting + run: dart --disable-analytics format --set-exit-if-changed frontend/app_student/lib + + - name: Lint + run: flutter analyze frontend/app_student/lib \ No newline at end of file diff --git a/backend/.php-cs-fixer.cache b/backend/.php-cs-fixer.cache new file mode 100644 index 0000000..88bf9ce --- /dev/null +++ b/backend/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.2.12","version":"3.51.0:v3.51.0#127fa74f010da99053e3f5b62672615b72dd6efd","indent":" ","lineEnding":"\n","rules":{"align_multiline_comment":true,"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"class_reference_name_casing":true,"clean_namespace":true,"concat_space":true,"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_declaration":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":false,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["attribute","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_hidden_params":true,"remove_inheritdoc":true},"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"nullable_type_declaration":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"ordered_types":{"null_adjustment":"always_last","sort_algorithm":"none"},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":{"groups":[["Annotation","NamedArgumentConstructor","Target"],["author","copyright","license"],["category","package","subpackage"],["property","property-read","property-write"],["deprecated","link","see","since"]]},"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_around_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"statement_indentation":{"stick_comment_to_next_continuous_control_statement":true},"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"type_declaration_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"cast_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\\Controller\\Api\\ClassesController.php":"3edd9a69cd0992943c7f4e35e189a328","src\\Controller\\Api\\TimetableController.php":"c9b1d63edf22527d74b68c59b9bd06eb","src\\Entity\\ClassGroups.php":"6bc6445daf9e43c60078656be46a06d4","src\\Entity\\DaySchedule.php":"56b4524fd686bf525de63858705ecfb2","src\\Entity\\Event.php":"6cbed0ffedbaccef2e8935c1a584977b","src\\Entity\\EventHours.php":"b437c36942eca2170869a2a7aaf2fb70","src\\Entity\\WeekSchedule.php":"a622ed2509b48533d6d6bc79a42dbdcc","src\\Kernel.php":"065c02fc2d62bfff3cd16f3a0b46d9ee","src\\Repository\\ClassGroupsRepository.php":"92142c8ee810ca31f60bffab20a87b0d","src\\Repository\\DayScheduleRepository.php":"98703542528a91067ab9ce94728d37d2","src\\Repository\\EventHoursRepository.php":"ae0ae56be4254d2d2e0d0dda12c03723","src\\Repository\\EventRepository.php":"8be5088a65ffa6accaf4ccdbfb2f203b","src\\Repository\\WeekScheduleRepository.php":"49e3ea686a24c1193a983c024d08d22c","src\\Service\\ClassesScraperService.php":"d1961c5983c33c3fb970892a4976baa7","src\\Service\\JsonService.php":"130ad9ecda589dd98c6788ee552b66c1","src\\Service\\TimetableService.php":"efc4917fd4b881eac4e2c6d481b38904","src\\Service\\XMLService.php":"8db6a1d71c24b578809ad0793f2bd9d6","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder\\.php-cs-fixer.dist.php":"4a5cd9e046891dc468fde4a126674df1","C:\\Users\\phplu\\AppData\\Local\\Temp\\PHP CS Fixertemp_folder1\\.php-cs-fixer.dist.php":"4a5cd9e046891dc468fde4a126674df1"}} \ No newline at end of file diff --git a/backend/.php-cs-fixer.dist.php b/backend/.php-cs-fixer.dist.php new file mode 100644 index 0000000..3788194 --- /dev/null +++ b/backend/.php-cs-fixer.dist.php @@ -0,0 +1,13 @@ +in(__DIR__) + ->exclude('var') +; + +return (new PhpCsFixer\Config()) + ->setRules([ + '@Symfony' => true, + ]) + ->setFinder($finder) +; diff --git a/backend/compose.override.yaml b/backend/compose.override.yaml new file mode 100644 index 0000000..f2247d5 --- /dev/null +++ b/backend/compose.override.yaml @@ -0,0 +1,8 @@ +version: '3' + +services: +###> doctrine/doctrine-bundle ### + database: + ports: + - "5432" +###< doctrine/doctrine-bundle ### diff --git a/backend/compose.yaml b/backend/compose.yaml new file mode 100644 index 0000000..1abf6c6 --- /dev/null +++ b/backend/compose.yaml @@ -0,0 +1,21 @@ +version: '3' + +services: +###> doctrine/doctrine-bundle ### + database: + image: postgres:${POSTGRES_VERSION:-16}-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB:-app} + # You should definitely change the password in production + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} + POSTGRES_USER: ${POSTGRES_USER:-app} + volumes: + - database_data:/var/lib/postgresql/data:rw + # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! + # - ./docker/db/data:/var/lib/postgresql/data:rw +###< doctrine/doctrine-bundle ### + +volumes: +###> doctrine/doctrine-bundle ### + database_data: +###< doctrine/doctrine-bundle ### diff --git a/backend/config/packages/doctrine.yaml b/backend/config/packages/doctrine.yaml new file mode 100644 index 0000000..d42c52d --- /dev/null +++ b/backend/config/packages/doctrine.yaml @@ -0,0 +1,50 @@ +doctrine: + dbal: + url: '%env(resolve:DATABASE_URL)%' + + # IMPORTANT: You MUST configure your server version, + # either here or in the DATABASE_URL env var (see .env file) + #server_version: '16' + + profiling_collect_backtrace: '%kernel.debug%' + use_savepoints: true + orm: + auto_generate_proxy_classes: true + enable_lazy_ghost_objects: true + report_fields_where_declared: true + validate_xml_mapping: true + naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware + auto_mapping: true + mappings: + App: + type: attribute + is_bundle: false + dir: '%kernel.project_dir%/src/Entity' + prefix: 'App\Entity' + alias: App + +when@test: + doctrine: + dbal: + # "TEST_TOKEN" is typically set by ParaTest + dbname_suffix: '_test%env(default::TEST_TOKEN)%' + +when@prod: + doctrine: + orm: + auto_generate_proxy_classes: false + proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' + query_cache_driver: + type: pool + pool: doctrine.system_cache_pool + result_cache_driver: + type: pool + pool: doctrine.result_cache_pool + + framework: + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system diff --git a/backend/config/packages/doctrine_migrations.yaml b/backend/config/packages/doctrine_migrations.yaml new file mode 100644 index 0000000..29231d9 --- /dev/null +++ b/backend/config/packages/doctrine_migrations.yaml @@ -0,0 +1,6 @@ +doctrine_migrations: + migrations_paths: + # namespace is arbitrary but should be different from App\Migrations + # as migrations classes should NOT be autoloaded + 'DoctrineMigrations': '%kernel.project_dir%/migrations' + enable_profiler: false diff --git a/backend/migrations/.gitignore b/backend/migrations/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/backend/openapi.json b/backend/openapi.json new file mode 100644 index 0000000..5bb62bc --- /dev/null +++ b/backend/openapi.json @@ -0,0 +1,194 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "3il Schedule API", + "description": "API to access 3il schedule data" + }, + "servers": [ + { + "url": "https://api.lukasvalois.com", + "description": "Production server" + }, + { + "url": "https://api-dev.lukasvalois.com", + "description": "Testing server" + } + ], + "paths": { + "/api/classes": { + "get": { + "summary": "Get list of classes", + "responses": { + "200": { + "description": "List of classes", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "example": "B3 Groupe 1 IRC-FA" + } + } + } + } + } + } + } + }, + "/api/timetable": { + "get": { + "summary": "Get timetable for a given class", + "parameters": [ + { + "name": "class_param", + "in": "query", + "description": "Class parameter", + "required": true, + "schema": { + "type": "string", + "example": "B3 Groupe 1 IRC-FA" + } + } + ], + "responses": { + "200": { + "description": "Timetable for the given class", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Timetable" + } + } + } + }, + "404": { + "description": "No timetable found for the provided class", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Timetable": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 6479 + }, + "code": { + "type": "string", + "example": "24/10" + }, + "DaySchedule": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 476589 + }, + "date": { + "type": "string", + "format": "date-time", + "example": "2024-03-04T08:57:17+01:00" + }, + "jour": { + "type": "integer", + "example": 1 + }, + "events": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 29063 + }, + "creneau": { + "type": "integer", + "example": 1 + }, + "activite": { + "type": "string", + "example": "RSX HAUTE DISPO C" + }, + "couleur": { + "type": "string", + "example": "#FFFFFF;" + }, + "horaire": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "nullable": true + }, + "startAt": { + "type": "string", + "example": "8h30" + }, + "endAt": { + "type": "string", + "example": "10h" + } + } + }, + "salle": { + "type": "string", + "example": "206" + }, + "visio": { + "type": "boolean", + "example": false + }, + "daySchedule": { + "type": "string", + "example": "476589" + } + } + } + }, + "weekSchedule": { + "type": "string", + "example": "6479" + } + } + } + } + } + }, + "Error": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "No timetable found for the provided class" + } + } + } + } + } +} diff --git a/backend/src/Entity/ClassGroups.php b/backend/src/Entity/ClassGroups.php new file mode 100644 index 0000000..2becd07 --- /dev/null +++ b/backend/src/Entity/ClassGroups.php @@ -0,0 +1,50 @@ +id; + } + + public function getFile(): ?string + { + return $this->file; + } + + public function setFile(string $file): static + { + $this->file = $file; + + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } +} diff --git a/backend/src/Entity/DaySchedule.php b/backend/src/Entity/DaySchedule.php new file mode 100644 index 0000000..11f1b31 --- /dev/null +++ b/backend/src/Entity/DaySchedule.php @@ -0,0 +1,114 @@ +events = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function setId(int $id): static + { + $this->id = $id; + + return $this; + } + + public function getDate(): ?\DateTimeInterface + { + return $this->date; + } + + public function setDate(\DateTimeInterface $date): static + { + $this->date = $date; + + return $this; + } + + public function getJour(): ?int + { + return $this->jour; + } + + public function setJour(int $jour): static + { + $this->jour = $jour; + + return $this; + } + + /** + * @return Collection + */ + public function getEvents(): Collection + { + return $this->events; + } + + public function addEvent(Event $event): static + { + if (!$this->events->contains($event)) { + $this->events->add($event); + $event->setDaySchedule($this); + } + + return $this; + } + + public function removeEvent(Event $event): static + { + if ($this->events->removeElement($event)) { + // set the owning side to null (unless already changed) + if ($event->getDaySchedule() === $this) { + $event->setDaySchedule(null); + } + } + + return $this; + } + + public function getWeekSchedule(): ?WeekSchedule + { + return $this->weekSchedule; + } + + public function setWeekSchedule(?WeekSchedule $weekSchedule): static + { + $this->weekSchedule = $weekSchedule; + + return $this; + } +} diff --git a/backend/src/Entity/Event.php b/backend/src/Entity/Event.php new file mode 100644 index 0000000..938c168 --- /dev/null +++ b/backend/src/Entity/Event.php @@ -0,0 +1,134 @@ +id; + } + + public function getCreneau(): ?int + { + return $this->creneau; + } + + public function setCreneau(int $creneau): static + { + $this->creneau = $creneau; + + return $this; + } + + public function getActivite(): ?string + { + return $this->activite; + } + + public function setActivite(string $activite): static + { + $this->activite = $activite; + + return $this; + } + + public function setId(int $id): static + { + $this->id = $id; + + return $this; + } + + public function getCouleur(): ?string + { + return $this->couleur; + } + + public function setCouleur(string $couleur): static + { + $this->couleur = $couleur; + + return $this; + } + + public function getHoraire(): ?EventHours + { + return $this->horaire; + } + + public function setHoraire(EventHours $horaire): static + { + $this->horaire = $horaire; + + return $this; + } + + public function getSalle(): ?string + { + return $this->salle; + } + + public function setSalle(string $salle): static + { + $this->salle = $salle; + + return $this; + } + + public function isVisio(): ?bool + { + return $this->visio; + } + + public function setVisio(bool $visio): static + { + $this->visio = $visio; + + return $this; + } + + public function getDaySchedule(): ?DaySchedule + { + return $this->daySchedule; + } + + public function setDaySchedule(?DaySchedule $daySchedule): static + { + $this->daySchedule = $daySchedule; + + return $this; + } +} diff --git a/backend/src/Entity/EventHours.php b/backend/src/Entity/EventHours.php new file mode 100644 index 0000000..7a2a79d --- /dev/null +++ b/backend/src/Entity/EventHours.php @@ -0,0 +1,55 @@ +id; + } + + public function setId(?int $id): void + { + $this->id = $id; + } + + public function getStartAt(): ?string + { + return $this->startAt; + } + + public function setStartAt(string $startAt): static + { + $this->startAt = $startAt; + + return $this; + } + + public function getEndAt(): ?string + { + return $this->endAt; + } + + public function setEndAt(string $endAt): static + { + $this->endAt = $endAt; + + return $this; + } +} diff --git a/backend/src/Entity/WeekSchedule.php b/backend/src/Entity/WeekSchedule.php new file mode 100644 index 0000000..98dd553 --- /dev/null +++ b/backend/src/Entity/WeekSchedule.php @@ -0,0 +1,82 @@ +DaySchedule = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function setId(int $id): static + { + $this->id = $id; + + return $this; + } + + public function getCode(): ?string + { + return $this->code; + } + + public function setCode(string $code): static + { + $this->code = $code; + + return $this; + } + + /** + * @return Collection + */ + public function getDaySchedule(): Collection + { + return $this->DaySchedule; + } + + public function addDaySchedule(DaySchedule $daySchedule): static + { + if (!$this->DaySchedule->contains($daySchedule)) { + $this->DaySchedule->add($daySchedule); + $daySchedule->setWeekSchedule($this); + } + + return $this; + } + + public function removeDaySchedule(DaySchedule $daySchedule): static + { + if ($this->DaySchedule->removeElement($daySchedule)) { + // set the owning side to null (unless already changed) + if ($daySchedule->getWeekSchedule() === $this) { + $daySchedule->setWeekSchedule(null); + } + } + + return $this; + } +} diff --git a/backend/src/Repository/.gitignore b/backend/src/Repository/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/Repository/ClassGroupsRepository.php b/backend/src/Repository/ClassGroupsRepository.php new file mode 100644 index 0000000..366354a --- /dev/null +++ b/backend/src/Repository/ClassGroupsRepository.php @@ -0,0 +1,48 @@ + + * + * @method ClassGroups|null find($id, $lockMode = null, $lockVersion = null) + * @method ClassGroups|null findOneBy(array $criteria, array $orderBy = null) + * @method ClassGroups[] findAll() + * @method ClassGroups[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ClassGroupsRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ClassGroups::class); + } + + // /** + // * @return ClassGroups[] Returns an array of ClassGroups objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('c.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?ClassGroups + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/backend/src/Repository/DayScheduleRepository.php b/backend/src/Repository/DayScheduleRepository.php new file mode 100644 index 0000000..e06d64f --- /dev/null +++ b/backend/src/Repository/DayScheduleRepository.php @@ -0,0 +1,48 @@ + + * + * @method DaySchedule|null find($id, $lockMode = null, $lockVersion = null) + * @method DaySchedule|null findOneBy(array $criteria, array $orderBy = null) + * @method DaySchedule[] findAll() + * @method DaySchedule[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class DayScheduleRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, DaySchedule::class); + } + + // /** + // * @return DaySchedule[] Returns an array of DaySchedule objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('d') + // ->andWhere('d.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('d.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?DaySchedule + // { + // return $this->createQueryBuilder('d') + // ->andWhere('d.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/backend/src/Repository/EventHoursRepository.php b/backend/src/Repository/EventHoursRepository.php new file mode 100644 index 0000000..5f93506 --- /dev/null +++ b/backend/src/Repository/EventHoursRepository.php @@ -0,0 +1,48 @@ + + * + * @method EventHours|null find($id, $lockMode = null, $lockVersion = null) + * @method EventHours|null findOneBy(array $criteria, array $orderBy = null) + * @method EventHours[] findAll() + * @method EventHours[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class EventHoursRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, EventHours::class); + } + + // /** + // * @return EventHours[] Returns an array of EventHours objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('c.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?EventHours + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/backend/src/Repository/EventRepository.php b/backend/src/Repository/EventRepository.php new file mode 100644 index 0000000..c94262e --- /dev/null +++ b/backend/src/Repository/EventRepository.php @@ -0,0 +1,48 @@ + + * + * @method Event|null find($id, $lockMode = null, $lockVersion = null) + * @method Event|null findOneBy(array $criteria, array $orderBy = null) + * @method Event[] findAll() + * @method Event[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class EventRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Event::class); + } + + // /** + // * @return Event[] Returns an array of Event objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('e') + // ->andWhere('e.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('e.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Event + // { + // return $this->createQueryBuilder('e') + // ->andWhere('e.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/backend/src/Repository/WeekScheduleRepository.php b/backend/src/Repository/WeekScheduleRepository.php new file mode 100644 index 0000000..77fbf9e --- /dev/null +++ b/backend/src/Repository/WeekScheduleRepository.php @@ -0,0 +1,48 @@ + + * + * @method WeekSchedule|null find($id, $lockMode = null, $lockVersion = null) + * @method WeekSchedule|null findOneBy(array $criteria, array $orderBy = null) + * @method WeekSchedule[] findAll() + * @method WeekSchedule[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class WeekScheduleRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, WeekSchedule::class); + } + + // /** + // * @return WeekSchedule[] Returns an array of WeekSchedule objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('w') + // ->andWhere('w.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('w.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?WeekSchedule + // { + // return $this->createQueryBuilder('w') + // ->andWhere('w.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/backend/src/Service/JsonService.php b/backend/src/Service/JsonService.php new file mode 100644 index 0000000..4dac926 --- /dev/null +++ b/backend/src/Service/JsonService.php @@ -0,0 +1,35 @@ + function (object $object, string $format, array $context): string { + return $object->getId(); + }, + DateTimeNormalizer::FORMAT_KEY => [ + 'datetime' => 'Y-m-d', + 'datetime_immutable' => 'Y-m-d', + ], + ]; + + $normalizer = [new DateTimeNormalizer(), new ObjectNormalizer(null, null, null, null, null, null, $defaultContext)]; + + $serializer = new Serializer($normalizer, [$encoder]); + + return $serializer->serialize($entity, 'json'); + } +} diff --git a/backend/src/Service/XMLService.php b/backend/src/Service/XMLService.php new file mode 100644 index 0000000..6bba22d --- /dev/null +++ b/backend/src/Service/XMLService.php @@ -0,0 +1,41 @@ +request('GET', $xmlUrl)->getContent(); + } + + /** + * Parse XML data into an array. + * + * @param string $xmlContent The content of the XML file + */ + public function parseXmlData(string $xmlContent): array + { + $xmlHash = simplexml_load_string($xmlContent); + + return json_decode(json_encode($xmlHash), true); + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..535c776 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "symfony/serializer": "^7.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..da7ec8c --- /dev/null +++ b/composer.lock @@ -0,0 +1,193 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5097cf0fa95dfd7763908bae16d3c574", + "packages": [ + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "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-29T20:11:03+00:00" + }, + { + "name": "symfony/serializer", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb", + "reference": "c71d61c6c37804e10981960e5f5ebc2c8f0a4fbb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<6.4", + "symfony/property-access": "<6.4", + "symfony/property-info": "<6.4", + "symfony/uid": "<6.4", + "symfony/validator": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "seld/jsonlint": "^1.10", + "symfony/cache": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "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": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/serializer/tree/v7.0.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-22T20:27:20+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/frontend/app_student/lib/api/class_groups/entities/class_group_entity.dart b/frontend/app_student/lib/api/class_groups/entities/class_group_entity.dart new file mode 100644 index 0000000..27de29c --- /dev/null +++ b/frontend/app_student/lib/api/class_groups/entities/class_group_entity.dart @@ -0,0 +1,13 @@ +class ClassGroupEntity { + final String file; + final String name; + + ClassGroupEntity({required this.file, required this.name}); + + factory ClassGroupEntity.fromJson(Map json) { + return ClassGroupEntity( + file: json['file'], + name: json['name'], + ); + } +} diff --git a/frontend/app_student/lib/api/class_groups/models/class_group_model.dart b/frontend/app_student/lib/api/class_groups/models/class_group_model.dart new file mode 100644 index 0000000..e9f55da --- /dev/null +++ b/frontend/app_student/lib/api/class_groups/models/class_group_model.dart @@ -0,0 +1,10 @@ +import '../entities/class_group_entity.dart'; + +class ClassGroupModel { + final ClassGroupEntity classGroup; + + ClassGroupModel({required this.classGroup}); + + String get name => classGroup.name; + String get file => classGroup.file; +} diff --git a/frontend/app_student/lib/api/class_groups/repositories/class_group_repository.dart b/frontend/app_student/lib/api/class_groups/repositories/class_group_repository.dart new file mode 100644 index 0000000..1250c9a --- /dev/null +++ b/frontend/app_student/lib/api/class_groups/repositories/class_group_repository.dart @@ -0,0 +1,16 @@ +import 'package:app_student/api/class_groups/entities/class_group_entity.dart'; +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/class_groups/models/class_group_model.dart'; + +class ClassGroupRepository { + final ApiService apiService; + + ClassGroupRepository({required this.apiService}); + + Future> getClasses() { + return apiService.getData('/api/classes', (item) { + final entity = ClassGroupEntity.fromJson(item); + return ClassGroupModel(classGroup: entity); + }); + } +} diff --git a/frontend/app_student/lib/api/day_schedule/entities/day_schedule_entity.dart b/frontend/app_student/lib/api/day_schedule/entities/day_schedule_entity.dart new file mode 100644 index 0000000..6da93e5 --- /dev/null +++ b/frontend/app_student/lib/api/day_schedule/entities/day_schedule_entity.dart @@ -0,0 +1,27 @@ +import '../../events/entities/event_entity.dart'; + +class DayScheduleEntity { + final DateTime date; + final int jour; + final List events; + + DayScheduleEntity({ + required this.date, + required this.jour, + required this.events, + }); + + factory DayScheduleEntity.fromJson(Map json) { + var eventsFromJson = json['events'] as List; + List eventsList = + eventsFromJson.map((i) => EventEntity.fromJson(i)).toList(); + + DateTime date = DateTime.parse(json['date']); + + return DayScheduleEntity( + date: date, + jour: json['jour'], + events: eventsList, + ); + } +} diff --git a/frontend/app_student/lib/api/day_schedule/models/day_schedule_model.dart b/frontend/app_student/lib/api/day_schedule/models/day_schedule_model.dart new file mode 100644 index 0000000..149c410 --- /dev/null +++ b/frontend/app_student/lib/api/day_schedule/models/day_schedule_model.dart @@ -0,0 +1,14 @@ +import '../../events/entities/event_entity.dart'; +import '../entities/day_schedule_entity.dart'; + +class DayScheduleModel { + final DayScheduleEntity entity; + + DayScheduleModel({required this.entity}); + + DateTime get date => entity.date; + + int get jour => entity.jour; + + List get events => entity.events; +} diff --git a/frontend/app_student/lib/api/event_hours/entities/event_hours_entity.dart b/frontend/app_student/lib/api/event_hours/entities/event_hours_entity.dart new file mode 100644 index 0000000..8f0a23c --- /dev/null +++ b/frontend/app_student/lib/api/event_hours/entities/event_hours_entity.dart @@ -0,0 +1,16 @@ +class EventHoursEntity { + final String startAt; + final String endAt; + + EventHoursEntity({ + required this.startAt, + required this.endAt, + }); + + factory EventHoursEntity.fromJson(Map json) { + return EventHoursEntity( + startAt: json['startAt'], + endAt: json['endAt'], + ); + } +} diff --git a/frontend/app_student/lib/api/event_hours/models/event_hours_model.dart b/frontend/app_student/lib/api/event_hours/models/event_hours_model.dart new file mode 100644 index 0000000..eb4336e --- /dev/null +++ b/frontend/app_student/lib/api/event_hours/models/event_hours_model.dart @@ -0,0 +1,10 @@ +import '../entities/event_hours_entity.dart'; + +class EventHoursModel { + final EventHoursEntity entity; + + EventHoursModel({required this.entity}); + + String get startAt => entity.startAt; + String get endAt => entity.endAt; +} diff --git a/frontend/app_student/lib/api/events/entities/event_entity.dart b/frontend/app_student/lib/api/events/entities/event_entity.dart new file mode 100644 index 0000000..a5baeac --- /dev/null +++ b/frontend/app_student/lib/api/events/entities/event_entity.dart @@ -0,0 +1,33 @@ +import '../../event_hours/entities/event_hours_entity.dart'; + +class EventEntity { + final int id; + final int creneau; + final String activite; + final String couleur; + final EventHoursEntity horaires; + final String salle; + final bool visio; + + EventEntity({ + required this.id, + required this.creneau, + required this.activite, + required this.couleur, + required this.horaires, + required this.salle, + required this.visio, + }); + + factory EventEntity.fromJson(Map json) { + return EventEntity( + creneau: json['creneau'] ?? 'null', + activite: json['activite'] ?? 'null', + id: json['id'] ?? 'null', + couleur: json['couleur'] ?? 'null', + horaires: EventHoursEntity.fromJson(json['horaire']), + salle: json['salle'] ?? 'null', + visio: json['visio'] ?? false, + ); + } +} diff --git a/frontend/app_student/lib/api/events/models/event_model.dart b/frontend/app_student/lib/api/events/models/event_model.dart new file mode 100644 index 0000000..b0fa2e9 --- /dev/null +++ b/frontend/app_student/lib/api/events/models/event_model.dart @@ -0,0 +1,15 @@ +import '../../event_hours/models/event_hours_model.dart'; +import '../entities/event_entity.dart'; + +class EventModel { + final EventEntity entity; + + EventModel({required this.entity}); + + int get id => entity.id; + int get creneau => entity.creneau; + String get activite => entity.activite; + String get couleur => entity.couleur; + EventHoursModel get horaires => EventHoursModel(entity: entity.horaires); + String get salle => entity.salle; +} diff --git a/frontend/app_student/lib/api/users/entities/user_entity.dart b/frontend/app_student/lib/api/users/entities/user_entity.dart new file mode 100644 index 0000000..5acce5b --- /dev/null +++ b/frontend/app_student/lib/api/users/entities/user_entity.dart @@ -0,0 +1,14 @@ +import 'package:app_student/api/class_groups/entities/class_group_entity.dart'; + +class UserEntity { + final String ine; + final String firstName; + final DateTime birthDate; + final ClassGroupEntity? classGroup; + + UserEntity( + {required this.ine, + required this.firstName, + required this.birthDate, + this.classGroup}); +} diff --git a/frontend/app_student/lib/api/users/models/user_model.dart b/frontend/app_student/lib/api/users/models/user_model.dart new file mode 100644 index 0000000..e8a8836 --- /dev/null +++ b/frontend/app_student/lib/api/users/models/user_model.dart @@ -0,0 +1,11 @@ +import '../entities/user_entity.dart'; + +class UserModel { + final UserEntity entity; + + UserModel({required this.entity}); + + String get file => entity.ine; + String get name => entity.firstName; + DateTime get birthDate => entity.birthDate; +} diff --git a/frontend/app_student/lib/api/week_schedule/entities/week_schedule_entity.dart b/frontend/app_student/lib/api/week_schedule/entities/week_schedule_entity.dart new file mode 100644 index 0000000..a56c5d8 --- /dev/null +++ b/frontend/app_student/lib/api/week_schedule/entities/week_schedule_entity.dart @@ -0,0 +1,21 @@ +import 'package:app_student/api/day_schedule/entities/day_schedule_entity.dart'; + +class WeekScheduleEntity { + final String code; + final List daySchedules; + + WeekScheduleEntity({ + required this.code, + required this.daySchedules, + }); + + factory WeekScheduleEntity.fromJson(Map json) { + return WeekScheduleEntity( + code: json['code'], + daySchedules: (json['DaySchedule'] as List) + .map((daySchedule) => + DayScheduleEntity.fromJson(daySchedule as Map)) + .toList(), + ); + } +} diff --git a/frontend/app_student/lib/api/week_schedule/models/week_schedule_model.dart b/frontend/app_student/lib/api/week_schedule/models/week_schedule_model.dart new file mode 100644 index 0000000..279723d --- /dev/null +++ b/frontend/app_student/lib/api/week_schedule/models/week_schedule_model.dart @@ -0,0 +1,14 @@ +import 'package:app_student/api/day_schedule/entities/day_schedule_entity.dart'; + +import '../entities/week_schedule_entity.dart'; + +class WeekScheduleModel { + final WeekScheduleEntity weekSchedule; + + WeekScheduleModel({ + required this.weekSchedule, + }); + + String get code => weekSchedule.code; + List get daySchedules => weekSchedule.daySchedules; +} diff --git a/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repositories.dart b/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repositories.dart new file mode 100644 index 0000000..39e3b53 --- /dev/null +++ b/frontend/app_student/lib/api/week_schedule/repositories/week_schedule_repositories.dart @@ -0,0 +1,16 @@ +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/week_schedule/entities/week_schedule_entity.dart'; +import 'package:app_student/api/week_schedule/models/week_schedule_model.dart'; + +class WeekScheduleRepository { + final ApiService apiService; + + WeekScheduleRepository({required this.apiService, required String className}); + + Future> getWeeksSchedule(className) { + return apiService.getData('/api/timetable?class_param=$className', (item) { + final entity = WeekScheduleEntity.fromJson(item); + return WeekScheduleModel(weekSchedule: entity); + }); + } +} diff --git a/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart b/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart new file mode 100644 index 0000000..cd2c584 --- /dev/null +++ b/frontend/app_student/lib/class_groups/cubit/class_group_cubit.dart @@ -0,0 +1,22 @@ +import 'package:app_student/api/class_groups/models/class_group_model.dart'; +import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +part 'class_group_state.dart'; + +class ClassGroupCubit extends Cubit { + final ClassGroupRepository classRepository; + + ClassGroupCubit({required this.classRepository}) : super(ClassGroupInitial()); + + Future fetchClasses() async { + try { + emit(ClassGroupLoading()); + final classes = await classRepository.getClasses(); + emit(ClassGroupLoaded(classes)); + } catch (e) { + emit(ClassGroupError(e.toString())); + } + } +} diff --git a/frontend/app_student/lib/class_groups/cubit/class_group_state.dart b/frontend/app_student/lib/class_groups/cubit/class_group_state.dart new file mode 100644 index 0000000..7ee2c23 --- /dev/null +++ b/frontend/app_student/lib/class_groups/cubit/class_group_state.dart @@ -0,0 +1,20 @@ +part of 'class_group_cubit.dart'; + +@immutable +abstract class ClassGroupState {} + +class ClassGroupInitial extends ClassGroupState {} + +class ClassGroupLoading extends ClassGroupState {} + +class ClassGroupLoaded extends ClassGroupState { + final List classes; + + ClassGroupLoaded(this.classes); +} + +class ClassGroupError extends ClassGroupState { + final String message; + + ClassGroupError(this.message); +} diff --git a/frontend/app_student/lib/class_groups/views/class_group_view.dart b/frontend/app_student/lib/class_groups/views/class_group_view.dart new file mode 100644 index 0000000..91e9d7f --- /dev/null +++ b/frontend/app_student/lib/class_groups/views/class_group_view.dart @@ -0,0 +1,45 @@ +import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:app_student/class_groups/cubit/class_group_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ClassListPage extends StatelessWidget { + const ClassListPage({super.key}); + + @override + Widget build(BuildContext context) { + final classRepository = + RepositoryProvider.of(context); + final classCubit = ClassGroupCubit(classRepository: classRepository); + + return BlocProvider( + create: (context) => classCubit..fetchClasses(), + child: Scaffold( + appBar: AppBar( + title: const Text('Class List'), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is ClassGroupLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is ClassGroupLoaded) { + return ListView.builder( + itemCount: state.classes.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(state.classes[index].name), + subtitle: Text(state.classes[index].file), + ); + }, + ); + } else if (state is ClassGroupError) { + return Center(child: Text(state.message)); + } else { + return const SizedBox.shrink(); + } + }, + ), + ), + ); + } +} diff --git a/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart b/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart new file mode 100644 index 0000000..ff8d5ff --- /dev/null +++ b/frontend/app_student/lib/week_schedule/cubit/week_schedule_cubit.dart @@ -0,0 +1,23 @@ +import 'package:app_student/api/week_schedule/repositories/week_schedule_repositories.dart'; +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +part 'week_schedule_state.dart'; + +class WeekScheduleCubit extends Cubit { + final WeekScheduleRepository weekScheduleRepository; + + WeekScheduleCubit({required this.weekScheduleRepository}) + : super(WeekScheduleInitial()); + + Future fetchWeekSchedule(String className) async { + try { + emit(WeekScheduleLoading()); + final weekSchedule = + await weekScheduleRepository.getWeeksSchedule(className); + emit(WeekScheduleLoaded(weekSchedule)); + } catch (e) { + emit(WeekScheduleError(e.toString())); + } + } +} diff --git a/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart b/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart new file mode 100644 index 0000000..cea2f6f --- /dev/null +++ b/frontend/app_student/lib/week_schedule/cubit/week_schedule_state.dart @@ -0,0 +1,20 @@ +part of 'week_schedule_cubit.dart'; + +@immutable +abstract class WeekScheduleState {} + +class WeekScheduleInitial extends WeekScheduleState {} + +class WeekScheduleLoading extends WeekScheduleState {} + +class WeekScheduleLoaded extends WeekScheduleState { + final List weekSchedule; + + WeekScheduleLoaded(this.weekSchedule); +} + +class WeekScheduleError extends WeekScheduleState { + final String message; + + WeekScheduleError(this.message); +} diff --git a/frontend/app_student/lib/week_schedule/views/week_schedule.dart b/frontend/app_student/lib/week_schedule/views/week_schedule.dart new file mode 100644 index 0000000..3041815 --- /dev/null +++ b/frontend/app_student/lib/week_schedule/views/week_schedule.dart @@ -0,0 +1,83 @@ +import 'package:app_student/api/day_schedule/entities/day_schedule_entity.dart'; +import 'package:app_student/api/week_schedule/models/week_schedule_model.dart'; +import 'package:app_student/api/week_schedule/repositories/week_schedule_repositories.dart'; +import 'package:app_student/week_schedule/cubit/week_schedule_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class DayScheduleWidget extends StatelessWidget { + final DayScheduleEntity daySchedule; + + const DayScheduleWidget({super.key, required this.daySchedule}); + + @override + Widget build(BuildContext context) { + return ExpansionTile( + title: Text('Date: ${daySchedule.date}'), + children: daySchedule.events.map((event) { + return ListTile( + title: Text('Event: ${event.activite}'), + subtitle: Text( + 'Start at: ${event.horaires.startAt}, End Time: ${event.horaires.endAt}'), + ); + }).toList(), + ); + } +} + +class WeekScheduleWidget extends StatelessWidget { + final WeekScheduleModel weekSchedule; + + const WeekScheduleWidget({super.key, required this.weekSchedule}); + + @override + Widget build(BuildContext context) { + return ExpansionTile( + title: Text('Code: ${weekSchedule.code}'), + children: weekSchedule.daySchedules.map((daySchedule) { + return DayScheduleWidget(daySchedule: daySchedule); + }).toList(), + ); + } +} + +class WeekSchedulePage extends StatelessWidget { + const WeekSchedulePage({super.key}); + + @override + Widget build(BuildContext context) { + final weekScheduleRepository = + RepositoryProvider.of(context); + final weekScheduleCubit = + WeekScheduleCubit(weekScheduleRepository: weekScheduleRepository); + + return BlocProvider( + create: (context) => + weekScheduleCubit..fetchWeekSchedule('B3 Groupe 3 DLW-FA'), + child: Scaffold( + appBar: AppBar( + title: const Text('Week Schedule'), + ), + body: BlocBuilder( + builder: (context, state) { + if (state is WeekScheduleLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is WeekScheduleLoaded) { + return ListView.builder( + itemCount: state.weekSchedule.length, + itemBuilder: (context, index) { + final weekSchedule = state.weekSchedule[index]; + return WeekScheduleWidget(weekSchedule: weekSchedule); + }, + ); + } else if (state is WeekScheduleError) { + return Center(child: Text(state.message)); + } else { + return const SizedBox.shrink(); + } + }, + ), + ), + ); + } +} From e26d35ec3397f742baee244b296390a92fcfbc0a Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:37:40 +0100 Subject: [PATCH 05/10] =?UTF-8?q?=E2=9E=95:=20added=20flutter=20bloc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Flutter/GeneratedPluginRegistrant.swift | 2 + frontend/app_student/pubspec.lock | 151 +++++++++++++++++- frontend/app_student/pubspec.yaml | 1 + 3 files changed, 153 insertions(+), 1 deletion(-) diff --git a/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..724bb2a 100644 --- a/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/app_student/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/frontend/app_student/pubspec.lock b/frontend/app_student/pubspec.lock index b5fb585..b6fa40d 100644 --- a/frontend/app_student/pubspec.lock +++ b/frontend/app_student/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e + url: "https://pub.dev" + source: hosted + version: "8.1.3" boolean_selector: dependency: transitive description: @@ -57,11 +65,35 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1" + url: "https://pub.dev" + source: hosted + version: "8.1.4" flutter_lints: dependency: "direct dev" description: @@ -75,6 +107,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" http: dependency: "direct main" description: @@ -163,6 +200,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" provider: dependency: "direct main" description: @@ -171,6 +248,62 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter @@ -256,6 +389,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" + url: "https://pub.dev" + source: hosted + version: "5.3.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" sdks: dart: ">=3.3.0 <4.0.0" - flutter: ">=1.16.0" + flutter: ">=3.19.0" diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index 257dbc8..0ef7061 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: provider: ^6.1.2 bloc: ^8.1.3 shared_preferences: ^2.2.2 + flutter_bloc: ^8.1.4 dev_dependencies: flutter_test: From 0b4540622e4130de8d233b882f58b5c77ce10efd Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:18:54 +0100 Subject: [PATCH 06/10] =?UTF-8?q?=F0=9F=9A=A7:=20added=20shared=20preferen?= =?UTF-8?q?ces=20to=20login=20page=20but=20not=20redirecting=20to=20class?= =?UTF-8?q?=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/login/cubit/login_cubit.dart | 40 ++++++++++- .../lib/login/cubit/login_state.dart | 6 ++ .../lib/login/views/login_page.dart | 72 ++++++++++++++++--- .../lib/login/widgets/form/button_submit.dart | 23 +++++- .../lib/login/widgets/form/form_login.dart | 25 +++++-- .../widgets/form/inputs/input_birthdate.dart | 7 +- .../login/widgets/form/inputs/input_ine.dart | 5 +- .../widgets/form/inputs/input_prenom.dart | 5 +- .../lib/login/widgets/header/header_logo.dart | 2 +- frontend/app_student/lib/main_dev.dart | 20 ++++-- frontend/app_student/pubspec.lock | 2 +- frontend/app_student/pubspec.yaml | 3 +- 12 files changed, 180 insertions(+), 30 deletions(-) diff --git a/frontend/app_student/lib/login/cubit/login_cubit.dart b/frontend/app_student/lib/login/cubit/login_cubit.dart index b394666..631c84a 100644 --- a/frontend/app_student/lib/login/cubit/login_cubit.dart +++ b/frontend/app_student/lib/login/cubit/login_cubit.dart @@ -1,8 +1,46 @@ import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'login_state.dart'; class LoginCubit extends Cubit { - LoginCubit() : super(LoginInitial()); + LoginCubit() : super(LoginInitial()) { + checkUserAuthentication(); + } + + Future areFieldsFilled( + String ine, String name, String birthDate) async { + if (ine.isEmpty || name.isEmpty || birthDate.isEmpty) { + return false; + } + return true; + } + + Future saveLoginDetails( + String ine, String name, String birthDate) async { + if (!(await areFieldsFilled(ine, name, birthDate))) { + emit(LoginFieldError()); + return; + } + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('ine', ine); + await prefs.setString('name', name); + await prefs.setString('birthDate', birthDate); + emit(RedirectToClassSelection()); + } + + Future checkUserAuthentication() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? ine = prefs.getString('ine'); + String? name = prefs.getString('name'); + String? birthDate = prefs.getString('birthDate'); + String? className = prefs.getString('className'); + + if (ine != null && name != null && birthDate != null && className != null) { + emit(LoginAuthenticated()); + } else { + emit(LoginInitial()); + } + } } diff --git a/frontend/app_student/lib/login/cubit/login_state.dart b/frontend/app_student/lib/login/cubit/login_state.dart index 9dd6480..ead219e 100644 --- a/frontend/app_student/lib/login/cubit/login_state.dart +++ b/frontend/app_student/lib/login/cubit/login_state.dart @@ -4,3 +4,9 @@ part of 'login_cubit.dart'; abstract class LoginState {} class LoginInitial extends LoginState {} + +class LoginAuthenticated extends LoginState {} + +class LoginFieldError extends LoginState {} + +class RedirectToClassSelection extends LoginState {} diff --git a/frontend/app_student/lib/login/views/login_page.dart b/frontend/app_student/lib/login/views/login_page.dart index 506e802..594bb68 100644 --- a/frontend/app_student/lib/login/views/login_page.dart +++ b/frontend/app_student/lib/login/views/login_page.dart @@ -1,7 +1,16 @@ +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:app_student/config/config.dart'; +import 'package:app_student/login/cubit/login_cubit.dart'; import 'package:app_student/login/widgets/form/form_login.dart'; import 'package:app_student/login/widgets/header/header_text.dart'; +import 'package:app_student/week_schedule/views/week_schedule.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:provider/provider.dart'; +import '../../class_groups/cubit/class_group_cubit.dart'; +import '../../class_groups/views/class_group_view.dart'; import '../widgets/header/header_logo.dart'; class LoginPage extends StatelessWidget { @@ -9,14 +18,61 @@ class LoginPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: Column( - children: [ - HeaderLogo(), - HeaderText("Bonjour :)"), - Expanded(child: FormLogin()), - ], - ), + return BlocBuilder( + builder: (context, state) { + if (state is RedirectToClassSelection) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final config = Provider.of(context, listen: false); + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => BlocProvider( + create: (_) => ClassGroupCubit( + classRepository: ClassGroupRepository( + apiService: ApiService(apiUrl: config.apiUrl))), + child: const ClassListPage()), + ), + ); + }); + return Container(); + } else if (state is LoginInitial) { + return const Scaffold( + body: Column( + children: [ + HeaderLogo(), + HeaderText('Bonjour :)'), + Expanded(child: FormLogin()), + ], + ), + ); + } else if (state is LoginAuthenticated) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => const WeekSchedulePage()), + ); + }); + return Container(); + } else if (state is LoginFieldError) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Veuillez remplir tous les champs"), + backgroundColor: Colors.red, + ), + ); + }); + return const Scaffold( + body: Column( + children: [ + HeaderLogo(), + HeaderText('Bonjour :)'), + Expanded(child: FormLogin()), + ], + ), + ); + } else { + return Container(); + } + }, ); } } diff --git a/frontend/app_student/lib/login/widgets/form/button_submit.dart b/frontend/app_student/lib/login/widgets/form/button_submit.dart index eec155b..e4107a1 100644 --- a/frontend/app_student/lib/login/widgets/form/button_submit.dart +++ b/frontend/app_student/lib/login/widgets/form/button_submit.dart @@ -1,7 +1,18 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../cubit/login_cubit.dart'; class SubmitButton extends StatelessWidget { - const SubmitButton({super.key}); + final TextEditingController ineController; + final TextEditingController nameController; + final TextEditingController birthDateController; + + const SubmitButton( + {super.key, + required this.ineController, + required this.nameController, + required this.birthDateController}); @override Widget build(BuildContext context) { @@ -30,7 +41,15 @@ class SubmitButton extends StatelessWidget { ), ), ), - onPressed: () {}, + 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); + }, child: const Text('Connexion'), ), ), diff --git a/frontend/app_student/lib/login/widgets/form/form_login.dart b/frontend/app_student/lib/login/widgets/form/form_login.dart index b267116..c5e7790 100644 --- a/frontend/app_student/lib/login/widgets/form/form_login.dart +++ b/frontend/app_student/lib/login/widgets/form/form_login.dart @@ -5,18 +5,31 @@ import 'inputs/input_birthdate.dart'; import 'inputs/input_ine.dart'; import 'inputs/input_prenom.dart'; -class FormLogin extends StatelessWidget { +class FormLogin extends StatefulWidget { const FormLogin({super.key}); + @override + _FormLoginState createState() => _FormLoginState(); +} + +class _FormLoginState extends State { + final TextEditingController ineController = TextEditingController(); + final TextEditingController nameController = TextEditingController(); + final TextEditingController birthDateController = TextEditingController(); + @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( body: Column( children: [ - INETextField(), - BirthDateField(), - FirstnameTextField(), - SubmitButton() + INETextField(controller: ineController), + BirthDateField(controller: birthDateController), + FirstnameTextField(controller: nameController), + SubmitButton( + ineController: ineController, + nameController: nameController, + birthDateController: birthDateController, + ) ], ), ); diff --git a/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart b/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart index 1f5c99e..e159b4b 100644 --- a/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart +++ b/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class BirthDateField extends StatefulWidget { - const BirthDateField({super.key}); + final TextEditingController controller; + + const BirthDateField({super.key, required this.controller}); @override BirthDateFieldState createState() => BirthDateFieldState(); @@ -20,6 +22,8 @@ class BirthDateFieldState extends State { if (picked != null && picked != _selectedDate) { setState(() { _selectedDate = picked; + widget.controller.text = + _selectedDate.toLocal().toString().split(' ')[0]; }); } } @@ -40,6 +44,7 @@ class BirthDateFieldState extends State { ), const SizedBox(height: 10.0), TextFormField( + controller: widget.controller, readOnly: true, decoration: InputDecoration( hintText: _selectedDate.toLocal().toString().split(' ')[0], diff --git a/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart b/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart index d316681..775c7e7 100644 --- a/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart +++ b/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class INETextField extends StatelessWidget { - const INETextField({super.key}); + final TextEditingController controller; + + const INETextField({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -22,6 +24,7 @@ class INETextField extends StatelessWidget { ), const SizedBox(height: 10.0), TextFormField( + controller: controller, decoration: InputDecoration( hintText: 'Numéro INE', contentPadding: diff --git a/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart b/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart index d402f45..5e5cd24 100644 --- a/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart +++ b/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class FirstnameTextField extends StatelessWidget { - const FirstnameTextField({super.key}); + final TextEditingController controller; + + const FirstnameTextField({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -19,6 +21,7 @@ class FirstnameTextField extends StatelessWidget { ), const SizedBox(height: 10.0), TextFormField( + controller: controller, style: TextStyle( color: Colors.grey[600], ), diff --git a/frontend/app_student/lib/login/widgets/header/header_logo.dart b/frontend/app_student/lib/login/widgets/header/header_logo.dart index 08ad226..430dd84 100644 --- a/frontend/app_student/lib/login/widgets/header/header_logo.dart +++ b/frontend/app_student/lib/login/widgets/header/header_logo.dart @@ -12,7 +12,7 @@ class HeaderLogo extends StatelessWidget { child: SizedBox( width: 200.0, // Largeur de l'image height: 200.0, // Hauteur de l'image - child: Image.asset('images/3il-logo.jpg'), + child: Image.asset('assets/images/3il-logo.jpg'), ), ), ); diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart index e39fb78..017e3f9 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -1,9 +1,11 @@ import 'package:app_student/config/dev_config.dart'; -import 'package:app_student/login/views/login_page.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'config/config.dart'; +import 'login/cubit/login_cubit.dart'; +import 'login/views/login_page.dart'; void main() { runApp( @@ -20,11 +22,15 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Class List', - theme: ThemeData( - primarySwatch: Colors.blue, - focusColor: const Color(0xffE84E0F), - ), - home: const LoginPage()); + title: 'Class List', + theme: ThemeData( + primarySwatch: Colors.blue, + focusColor: const Color(0xffE84E0F), + ), + home: BlocProvider( + create: (context) => LoginCubit(), + child: const LoginPage(), + ), + ); } } diff --git a/frontend/app_student/pubspec.lock b/frontend/app_student/pubspec.lock index b6fa40d..0da147c 100644 --- a/frontend/app_student/pubspec.lock +++ b/frontend/app_student/pubspec.lock @@ -177,7 +177,7 @@ packages: source: hosted version: "0.8.0" meta: - dependency: transitive + dependency: "direct main" description: name: meta sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index 0ef7061..3df491d 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: bloc: ^8.1.3 shared_preferences: ^2.2.2 flutter_bloc: ^8.1.4 + meta: ^1.11.0 dev_dependencies: flutter_test: @@ -69,7 +70,7 @@ flutter: # - images/a_dot_ham.jpeg assets: - - assets/images/ + - assets/images/3il-logo.jpg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware From 5edee6d490afbec2d3b7f8b464c66a38a945f5ea Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:18:54 +0100 Subject: [PATCH 07/10] =?UTF-8?q?=E2=9C=A8:=20save=20login=20info=20into?= =?UTF-8?q?=20shared=20preferences=20and=20redirect=20to=20class=20select?= =?UTF-8?q?=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/login/cubit/login_cubit.dart | 40 ++++++++++- .../lib/login/cubit/login_state.dart | 6 ++ .../lib/login/views/login_page.dart | 72 ++++++++++++++++--- .../lib/login/widgets/form/button_submit.dart | 23 +++++- .../lib/login/widgets/form/form_login.dart | 25 +++++-- .../widgets/form/inputs/input_birthdate.dart | 7 +- .../login/widgets/form/inputs/input_ine.dart | 5 +- .../widgets/form/inputs/input_prenom.dart | 5 +- .../lib/login/widgets/header/header_logo.dart | 2 +- frontend/app_student/lib/main_dev.dart | 37 +++++++--- frontend/app_student/pubspec.lock | 2 +- frontend/app_student/pubspec.yaml | 3 +- 12 files changed, 195 insertions(+), 32 deletions(-) diff --git a/frontend/app_student/lib/login/cubit/login_cubit.dart b/frontend/app_student/lib/login/cubit/login_cubit.dart index b394666..631c84a 100644 --- a/frontend/app_student/lib/login/cubit/login_cubit.dart +++ b/frontend/app_student/lib/login/cubit/login_cubit.dart @@ -1,8 +1,46 @@ import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'login_state.dart'; class LoginCubit extends Cubit { - LoginCubit() : super(LoginInitial()); + LoginCubit() : super(LoginInitial()) { + checkUserAuthentication(); + } + + Future areFieldsFilled( + String ine, String name, String birthDate) async { + if (ine.isEmpty || name.isEmpty || birthDate.isEmpty) { + return false; + } + return true; + } + + Future saveLoginDetails( + String ine, String name, String birthDate) async { + if (!(await areFieldsFilled(ine, name, birthDate))) { + emit(LoginFieldError()); + return; + } + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('ine', ine); + await prefs.setString('name', name); + await prefs.setString('birthDate', birthDate); + emit(RedirectToClassSelection()); + } + + Future checkUserAuthentication() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? ine = prefs.getString('ine'); + String? name = prefs.getString('name'); + String? birthDate = prefs.getString('birthDate'); + String? className = prefs.getString('className'); + + if (ine != null && name != null && birthDate != null && className != null) { + emit(LoginAuthenticated()); + } else { + emit(LoginInitial()); + } + } } diff --git a/frontend/app_student/lib/login/cubit/login_state.dart b/frontend/app_student/lib/login/cubit/login_state.dart index 9dd6480..ead219e 100644 --- a/frontend/app_student/lib/login/cubit/login_state.dart +++ b/frontend/app_student/lib/login/cubit/login_state.dart @@ -4,3 +4,9 @@ part of 'login_cubit.dart'; abstract class LoginState {} class LoginInitial extends LoginState {} + +class LoginAuthenticated extends LoginState {} + +class LoginFieldError extends LoginState {} + +class RedirectToClassSelection extends LoginState {} diff --git a/frontend/app_student/lib/login/views/login_page.dart b/frontend/app_student/lib/login/views/login_page.dart index 506e802..594bb68 100644 --- a/frontend/app_student/lib/login/views/login_page.dart +++ b/frontend/app_student/lib/login/views/login_page.dart @@ -1,7 +1,16 @@ +import 'package:app_student/api/api_service.dart'; +import 'package:app_student/api/class_groups/repositories/class_group_repository.dart'; +import 'package:app_student/config/config.dart'; +import 'package:app_student/login/cubit/login_cubit.dart'; import 'package:app_student/login/widgets/form/form_login.dart'; import 'package:app_student/login/widgets/header/header_text.dart'; +import 'package:app_student/week_schedule/views/week_schedule.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:provider/provider.dart'; +import '../../class_groups/cubit/class_group_cubit.dart'; +import '../../class_groups/views/class_group_view.dart'; import '../widgets/header/header_logo.dart'; class LoginPage extends StatelessWidget { @@ -9,14 +18,61 @@ class LoginPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: Column( - children: [ - HeaderLogo(), - HeaderText("Bonjour :)"), - Expanded(child: FormLogin()), - ], - ), + return BlocBuilder( + builder: (context, state) { + if (state is RedirectToClassSelection) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final config = Provider.of(context, listen: false); + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => BlocProvider( + create: (_) => ClassGroupCubit( + classRepository: ClassGroupRepository( + apiService: ApiService(apiUrl: config.apiUrl))), + child: const ClassListPage()), + ), + ); + }); + return Container(); + } else if (state is LoginInitial) { + return const Scaffold( + body: Column( + children: [ + HeaderLogo(), + HeaderText('Bonjour :)'), + Expanded(child: FormLogin()), + ], + ), + ); + } else if (state is LoginAuthenticated) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => const WeekSchedulePage()), + ); + }); + return Container(); + } else if (state is LoginFieldError) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Veuillez remplir tous les champs"), + backgroundColor: Colors.red, + ), + ); + }); + return const Scaffold( + body: Column( + children: [ + HeaderLogo(), + HeaderText('Bonjour :)'), + Expanded(child: FormLogin()), + ], + ), + ); + } else { + return Container(); + } + }, ); } } diff --git a/frontend/app_student/lib/login/widgets/form/button_submit.dart b/frontend/app_student/lib/login/widgets/form/button_submit.dart index eec155b..e4107a1 100644 --- a/frontend/app_student/lib/login/widgets/form/button_submit.dart +++ b/frontend/app_student/lib/login/widgets/form/button_submit.dart @@ -1,7 +1,18 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../cubit/login_cubit.dart'; class SubmitButton extends StatelessWidget { - const SubmitButton({super.key}); + final TextEditingController ineController; + final TextEditingController nameController; + final TextEditingController birthDateController; + + const SubmitButton( + {super.key, + required this.ineController, + required this.nameController, + required this.birthDateController}); @override Widget build(BuildContext context) { @@ -30,7 +41,15 @@ class SubmitButton extends StatelessWidget { ), ), ), - onPressed: () {}, + 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); + }, child: const Text('Connexion'), ), ), diff --git a/frontend/app_student/lib/login/widgets/form/form_login.dart b/frontend/app_student/lib/login/widgets/form/form_login.dart index b267116..c5e7790 100644 --- a/frontend/app_student/lib/login/widgets/form/form_login.dart +++ b/frontend/app_student/lib/login/widgets/form/form_login.dart @@ -5,18 +5,31 @@ import 'inputs/input_birthdate.dart'; import 'inputs/input_ine.dart'; import 'inputs/input_prenom.dart'; -class FormLogin extends StatelessWidget { +class FormLogin extends StatefulWidget { const FormLogin({super.key}); + @override + _FormLoginState createState() => _FormLoginState(); +} + +class _FormLoginState extends State { + final TextEditingController ineController = TextEditingController(); + final TextEditingController nameController = TextEditingController(); + final TextEditingController birthDateController = TextEditingController(); + @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( body: Column( children: [ - INETextField(), - BirthDateField(), - FirstnameTextField(), - SubmitButton() + INETextField(controller: ineController), + BirthDateField(controller: birthDateController), + FirstnameTextField(controller: nameController), + SubmitButton( + ineController: ineController, + nameController: nameController, + birthDateController: birthDateController, + ) ], ), ); diff --git a/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart b/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart index 1f5c99e..e159b4b 100644 --- a/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart +++ b/frontend/app_student/lib/login/widgets/form/inputs/input_birthdate.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class BirthDateField extends StatefulWidget { - const BirthDateField({super.key}); + final TextEditingController controller; + + const BirthDateField({super.key, required this.controller}); @override BirthDateFieldState createState() => BirthDateFieldState(); @@ -20,6 +22,8 @@ class BirthDateFieldState extends State { if (picked != null && picked != _selectedDate) { setState(() { _selectedDate = picked; + widget.controller.text = + _selectedDate.toLocal().toString().split(' ')[0]; }); } } @@ -40,6 +44,7 @@ class BirthDateFieldState extends State { ), const SizedBox(height: 10.0), TextFormField( + controller: widget.controller, readOnly: true, decoration: InputDecoration( hintText: _selectedDate.toLocal().toString().split(' ')[0], diff --git a/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart b/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart index d316681..775c7e7 100644 --- a/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart +++ b/frontend/app_student/lib/login/widgets/form/inputs/input_ine.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class INETextField extends StatelessWidget { - const INETextField({super.key}); + final TextEditingController controller; + + const INETextField({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -22,6 +24,7 @@ class INETextField extends StatelessWidget { ), const SizedBox(height: 10.0), TextFormField( + controller: controller, decoration: InputDecoration( hintText: 'Numéro INE', contentPadding: diff --git a/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart b/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart index d402f45..5e5cd24 100644 --- a/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart +++ b/frontend/app_student/lib/login/widgets/form/inputs/input_prenom.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class FirstnameTextField extends StatelessWidget { - const FirstnameTextField({super.key}); + final TextEditingController controller; + + const FirstnameTextField({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -19,6 +21,7 @@ class FirstnameTextField extends StatelessWidget { ), const SizedBox(height: 10.0), TextFormField( + controller: controller, style: TextStyle( color: Colors.grey[600], ), diff --git a/frontend/app_student/lib/login/widgets/header/header_logo.dart b/frontend/app_student/lib/login/widgets/header/header_logo.dart index 08ad226..430dd84 100644 --- a/frontend/app_student/lib/login/widgets/header/header_logo.dart +++ b/frontend/app_student/lib/login/widgets/header/header_logo.dart @@ -12,7 +12,7 @@ class HeaderLogo extends StatelessWidget { child: SizedBox( width: 200.0, // Largeur de l'image height: 200.0, // Hauteur de l'image - child: Image.asset('images/3il-logo.jpg'), + child: Image.asset('assets/images/3il-logo.jpg'), ), ), ); diff --git a/frontend/app_student/lib/main_dev.dart b/frontend/app_student/lib/main_dev.dart index e39fb78..1c55e87 100644 --- a/frontend/app_student/lib/main_dev.dart +++ b/frontend/app_student/lib/main_dev.dart @@ -1,14 +1,29 @@ import 'package:app_student/config/dev_config.dart'; -import 'package:app_student/login/views/login_page.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; +import 'api/api_service.dart'; +import 'api/class_groups/repositories/class_group_repository.dart'; import 'config/config.dart'; +import 'login/cubit/login_cubit.dart'; +import 'login/views/login_page.dart'; void main() { + final ClassGroupRepository classGroupRepository = ClassGroupRepository( + apiService: ApiService(apiUrl: DevConfig().apiUrl), + ); + runApp( - Provider( - create: (_) => DevConfig(), + MultiProvider( + providers: [ + Provider( + create: (_) => DevConfig(), + ), + RepositoryProvider( + create: (context) => classGroupRepository, + ), + ], child: const MyApp(), ), ); @@ -20,11 +35,15 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Class List', - theme: ThemeData( - primarySwatch: Colors.blue, - focusColor: const Color(0xffE84E0F), - ), - home: const LoginPage()); + title: 'Class List', + theme: ThemeData( + primarySwatch: Colors.blue, + focusColor: const Color(0xffE84E0F), + ), + home: BlocProvider( + create: (context) => LoginCubit(), + child: const LoginPage(), + ), + ); } } diff --git a/frontend/app_student/pubspec.lock b/frontend/app_student/pubspec.lock index b6fa40d..0da147c 100644 --- a/frontend/app_student/pubspec.lock +++ b/frontend/app_student/pubspec.lock @@ -177,7 +177,7 @@ packages: source: hosted version: "0.8.0" meta: - dependency: transitive + dependency: "direct main" description: name: meta sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 diff --git a/frontend/app_student/pubspec.yaml b/frontend/app_student/pubspec.yaml index 0ef7061..3df491d 100644 --- a/frontend/app_student/pubspec.yaml +++ b/frontend/app_student/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: bloc: ^8.1.3 shared_preferences: ^2.2.2 flutter_bloc: ^8.1.4 + meta: ^1.11.0 dev_dependencies: flutter_test: @@ -69,7 +70,7 @@ flutter: # - images/a_dot_ham.jpeg assets: - - assets/images/ + - assets/images/3il-logo.jpg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware From 6252991496d7d3041012a0b9b8a22679545de0c5 Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:33:16 +0100 Subject: [PATCH 08/10] =?UTF-8?q?=F0=9F=92=9A:=20dont=20upload=20code=20fo?= =?UTF-8?q?r=20pr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-backend-dev.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/deploy-backend-dev.yml b/.github/workflows/deploy-backend-dev.yml index 31008fb..a723df7 100644 --- a/.github/workflows/deploy-backend-dev.yml +++ b/.github/workflows/deploy-backend-dev.yml @@ -4,9 +4,6 @@ on: push: branches: - dev - pull_request: - branches: - - dev env: FLUTTER_VERSION: 3.10.1 From 33c7b205789cfe3db41118c75f3502c1126c40af Mon Sep 17 00:00:00 2001 From: Lukas <35173609+PHPLukaas@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:39:10 +0100 Subject: [PATCH 09/10] =?UTF-8?q?=F0=9F=9A=A8:=20Fix=20lint=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/app_student/lib/login/views/login_page.dart | 2 +- frontend/app_student/lib/login/widgets/form/form_login.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/app_student/lib/login/views/login_page.dart b/frontend/app_student/lib/login/views/login_page.dart index 594bb68..7c4c658 100644 --- a/frontend/app_student/lib/login/views/login_page.dart +++ b/frontend/app_student/lib/login/views/login_page.dart @@ -55,7 +55,7 @@ class LoginPage extends StatelessWidget { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text("Veuillez remplir tous les champs"), + content: Text('Veuillez remplir tous les champs'), backgroundColor: Colors.red, ), ); diff --git a/frontend/app_student/lib/login/widgets/form/form_login.dart b/frontend/app_student/lib/login/widgets/form/form_login.dart index c5e7790..3e8d51c 100644 --- a/frontend/app_student/lib/login/widgets/form/form_login.dart +++ b/frontend/app_student/lib/login/widgets/form/form_login.dart @@ -9,10 +9,10 @@ class FormLogin extends StatefulWidget { const FormLogin({super.key}); @override - _FormLoginState createState() => _FormLoginState(); + FormLoginState createState() => FormLoginState(); } -class _FormLoginState extends State { +class FormLoginState extends State { final TextEditingController ineController = TextEditingController(); final TextEditingController nameController = TextEditingController(); final TextEditingController birthDateController = TextEditingController(); From 974df4322e656cbf3fa52aa894ef4a6c06bad10f Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 13 Mar 2024 09:40:19 +0100 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=90=9B=EF=B8=8F:=20fixed=20overflow?= =?UTF-8?q?=20on=20login=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/login/widgets/form/form_login.dart | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/frontend/app_student/lib/login/widgets/form/form_login.dart b/frontend/app_student/lib/login/widgets/form/form_login.dart index 3e8d51c..93ff3e9 100644 --- a/frontend/app_student/lib/login/widgets/form/form_login.dart +++ b/frontend/app_student/lib/login/widgets/form/form_login.dart @@ -20,17 +20,19 @@ class FormLoginState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Column( - children: [ - INETextField(controller: ineController), - BirthDateField(controller: birthDateController), - FirstnameTextField(controller: nameController), - SubmitButton( - ineController: ineController, - nameController: nameController, - birthDateController: birthDateController, - ) - ], + body: SingleChildScrollView( + child: Column( + children: [ + INETextField(controller: ineController), + BirthDateField(controller: birthDateController), + FirstnameTextField(controller: nameController), + SubmitButton( + ineController: ineController, + nameController: nameController, + birthDateController: birthDateController, + ) + ], + ), ), ); }