From 82702e447aa2271c32099abd1c78b2e191e9612b Mon Sep 17 00:00:00 2001 From: tokujiTO Date: Thu, 23 Oct 2025 09:10:44 -0300 Subject: [PATCH 01/18] feat: add lock orientation to portrait --- lib/main.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/main.dart b/lib/main.dart index 23972bc..59cdf90 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,7 +11,12 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; Future main() async { + WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: ".env"); + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); runApp(DevicePreview(enabled: false, builder: (context) => const MyApp())); } From 91621db49fdca2c14b2a79db4fa0adea4ddee9f9 Mon Sep 17 00:00:00 2001 From: tokujiTO Date: Thu, 23 Oct 2025 09:11:06 -0300 Subject: [PATCH 02/18] fix: book not appearing if there is only assignments on the course --- lib/app/view/edit_page_view.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/app/view/edit_page_view.dart b/lib/app/view/edit_page_view.dart index d3de5e2..418d199 100644 --- a/lib/app/view/edit_page_view.dart +++ b/lib/app/view/edit_page_view.dart @@ -269,8 +269,10 @@ class _EditPageState extends State { MainAxisAlignment .spaceBetween, children: [ - widget.course.assignments! - .isEmpty + ((widget.course.assignments! + .isNotEmpty) && + (widget.course.exams! + .isEmpty)) ? IconButton( icon: const Icon( LucideIcons.bookOpen, From 0531c8feb00872128da1885bb628d5be0adfe90c Mon Sep 17 00:00:00 2001 From: tokujiTO Date: Thu, 23 Oct 2025 09:11:23 -0300 Subject: [PATCH 03/18] update: version number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ae342ab..9cc7024 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=3.3.4 <4.0.0" From 7d97199078450f20af55a6911de2b49c19aa458e Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 14:15:33 -0300 Subject: [PATCH 04/18] added new CI for testing new env variables passing via SSM --- .github/workflows/test_env_setup_dev.yaml | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/test_env_setup_dev.yaml diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml new file mode 100644 index 0000000..380b05d --- /dev/null +++ b/.github/workflows/test_env_setup_dev.yaml @@ -0,0 +1,69 @@ +name: Test ENV Setup + +on: + push: + branches: + - dev + +jobs: + test_envs: + environment: dev + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set AWS Account ID and other variables + run: | + if [[ "${{ github.ref_name }}" == "dev" ]]; then + echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_DEV }}" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "homolog" ]]; then + echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_HOML }}" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "prod" ]]; then + echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_PROD }}" >> $GITHUB_ENV + else + echo "Invalid branch name!" && exit 1 + fi + echo "STACK_NAME=DevMediasStack${{github.ref_name}}" >> $GITHUB_ENV + + - name: Setup AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ vars.AWS_REGION }} + role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/GithubActionsRole + role-session-name: github-action + + - name: Fetch config from SSM + run: | + STAGE=dev + + API_BASE=$(aws ssm get-parameter \ + --name "/devmedias/$STAGE/api/url" \ + --query "Parameter.Value" --output text) + + CDN_PLANS=$(aws ssm get-parameter \ + --name "/devmedias/$STAGE/cdn/plans" \ + --query "Parameter.Value" --output text) + + CDN_SUBJECTS=$(aws ssm get-parameter \ + --name "/devmedias/$STAGE/cdn/subjects" \ + --query "Parameter.Value" --output text) + + echo "API_BASE=$API_BASE" >> $GITHUB_ENV + echo "CDN_PLANS=$CDN_PLANS" >> $GITHUB_ENV + echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV + + - name: Create .env file + run: | + echo "API_COURSES=https://$CDN_PLANS" >> .env + echo "API_SUBJECTS=https://$CDN_SUBJECTS" >> .env + echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env + echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env + echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env + echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" >> .env + + - name: Print .env + run: cat .env \ No newline at end of file From 6c94fdcaa6db441c0731f0897a6c0e8fa50eeaf0 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 14:51:41 -0300 Subject: [PATCH 05/18] fixed api courses and subjects to fetch for json file instead of different cdns --- .github/workflows/test_env_setup_dev.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml index 380b05d..b7f040e 100644 --- a/.github/workflows/test_env_setup_dev.yaml +++ b/.github/workflows/test_env_setup_dev.yaml @@ -53,13 +53,12 @@ jobs: --query "Parameter.Value" --output text) echo "API_BASE=$API_BASE" >> $GITHUB_ENV - echo "CDN_PLANS=$CDN_PLANS" >> $GITHUB_ENV echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV - name: Create .env file run: | - echo "API_COURSES=https://$CDN_PLANS" >> .env - echo "API_SUBJECTS=https://$CDN_SUBJECTS" >> .env + echo "API_COURSES=https://$CDN_SUBJECTS/courses.json" >> .env + echo "API_SUBJECTS=https://$CDN_SUBJECTS/allSubjects.json" >> .env echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env From e44637924b80094916f14101245a6610a7276b99 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 18:20:25 -0300 Subject: [PATCH 06/18] updated env file generation and variable getting via ssm --- .github/workflows/build_android.yml | 73 +++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 732a13a..7a7b26d 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -18,8 +18,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - repository: ${{ inputs.repository }} - name: Get Version run: | @@ -29,12 +27,12 @@ jobs: echo "app_build_name=$app_build_name" >> $GITHUB_ENV - name: Setup Java Version - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 # era v1 with: - java-version: "12.x" + java-version: "17" # era 12, Flutter 3.24 exige 17+ - name: Setup Flutter Version - uses: subosito/flutter-action@v1 + uses: subosito/flutter-action@v2 # era v1 with: flutter-version: "3.24.3" @@ -48,6 +46,26 @@ jobs: bundler-cache: true working-directory: android + - name: Set AWS Account ID and other variables + run: | + if [[ "${{ github.ref_name }}" == "dev" ]]; then + echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_DEV }}" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "homolog" ]]; then + echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_HOML }}" >> $GITHUB_ENV + elif [[ "${{ github.ref_name }}" == "prod" ]]; then + echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_PROD }}" >> $GITHUB_ENV + else + echo "Invalid branch name!" && exit 1 + fi + echo "STACK_NAME=DevMediasStack${{github.ref_name}}" >> $GITHUB_ENV + + - name: Setup AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ vars.AWS_REGION }} + role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/GithubActionsRole + role-session-name: github-action + - name: Set envs run: | @@ -65,23 +83,50 @@ jobs: ANDROID_KEYSTORE_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_STORE_PASSWORD }} working-directory: android + # esse processo busca o endpoint do APIGW direto da variável setada pelo SSM configurado no back + # evitando essa passagem manual dessa endpoint pro secrets do github e evitando possíveis erros + # de passagem manual + + - name: Fetch config from SSM + run: | + STAGE=dev + + API_BASE=$(aws ssm get-parameter \ + --name "/devmedias/$STAGE/api/url" \ + --query "Parameter.Value" --output text) + + CDN_PLANS=$(aws ssm get-parameter \ + --name "/devmedias/$STAGE/cdn/plans" \ + --query "Parameter.Value" --output text) + + CDN_SUBJECTS=$(aws ssm get-parameter \ + --name "/devmedias/$STAGE/cdn/subjects" \ + --query "Parameter.Value" --output text) + + echo "API_BASE=$API_BASE" >> $GITHUB_ENV + echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV + - name: Create .env file run: | - echo "API_COURSES=${{vars.API_COURSES}}" >> .env # The courses API - echo "API_SUBJECTS=${{vars.API_SUBJECTS}}" >> .env # The subjects API - echo "API_GRADE_OPTMIZER=${{vars.API_GRADE_OPTMIZER}}" >> .env # The grade optimizer API - echo "API_SUPPORT_URL=${{vars.API_SUPPORT_URL}}" >> .env # The support API - echo "API_FINAL_SCORE_URL=${{vars.API_FINAL_SCORE_URL}}" >> .env # The final score API + echo "API_COURSES=https://$CDN_SUBJECTS/courses.json" >> .env + echo "API_SUBJECTS=https://$CDN_SUBJECTS/allSubjects.json" >> .env + echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env + echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env + echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env + echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" >> .env - name: Build Android run: | - flutter build appbundle --release --build-name=$app_build_name --build-number=$app_build_number - + flutter build appbundle --release \ + --build-name=$app_build_name \ + --build-number=$app_build_number + - name: Upload to artifacts uses: actions/upload-artifact@v4 with: name: app-release.aab path: build/app/outputs/bundle/release/ + retention-days: 1 deploy_to_play_store: needs: build_android @@ -102,12 +147,12 @@ jobs: - name: Setup Fastlane uses: ruby/setup-ruby@v1 with: - ruby-version: "2.6" + ruby-version: "3.2" bundler-cache: true working-directory: android - name: Exec fastlane - run: bundle exec fastlane ${{github.ref_name}} + run: bundle exec fastlane ${{ github.ref_name }} env: PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }} working-directory: android From 45920d6367e0d1671c2c77f38173921736f2e355 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 18:33:32 -0300 Subject: [PATCH 07/18] dont know what broke --- .github/workflows/test_env_setup_dev.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml index b7f040e..d3f747a 100644 --- a/.github/workflows/test_env_setup_dev.yaml +++ b/.github/workflows/test_env_setup_dev.yaml @@ -44,10 +44,6 @@ jobs: --name "/devmedias/$STAGE/api/url" \ --query "Parameter.Value" --output text) - CDN_PLANS=$(aws ssm get-parameter \ - --name "/devmedias/$STAGE/cdn/plans" \ - --query "Parameter.Value" --output text) - CDN_SUBJECTS=$(aws ssm get-parameter \ --name "/devmedias/$STAGE/cdn/subjects" \ --query "Parameter.Value" --output text) From d8f24063614eb1064362a164344301eb87aff5c6 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 18:54:41 -0300 Subject: [PATCH 08/18] updated env example --- .env.example | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 982c4bd..4665d16 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ -COURSES_URL= -GRADS_URL= -GRADE_OPTIMIZER_URL= +API_COURSES= +API_SUBJECTS= +API_GRADE_OPTMIZER= +API_GENETIC_ALGORITHM= +API_SUPPORT_URL= +API_FINAL_SCORE_URL= \ No newline at end of file From 837ee80e1291c0046cb3303de9028178a40e1413 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 18:57:57 -0300 Subject: [PATCH 09/18] updating test env setup dev CI --- .github/workflows/test_env_setup_dev.yaml | 33 ++++++--- android/app/build.gradle | 2 +- android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 4 +- .../controller/common/common_controller.dart | 4 +- .../common/common_controller.g.dart | 2 +- .../common/graduations_controller.dart | 1 - .../controller/common/support_controller.dart | 4 +- .../common/support_controller.g.dart | 2 +- .../controller/common/user_controller.dart | 2 - .../controller/popup_delete_controller.dart | 4 +- .../controller/popup_delete_controller.g.dart | 2 +- lib/app/view/edit_page_view.dart | 14 ++-- lib/app/view/home_page_view.dart | 2 +- lib/app/widgets/add_course_form.dart | 2 +- .../widgets/add_course_navigation_button.dart | 3 - .../widgets/common/navigation_top_bar.dart | 1 - lib/app/widgets/common/return_button.dart | 2 - lib/app/widgets/popup_delete_course.dart | 2 +- lib/app/widgets/starting_data_form.dart | 2 +- pubspec.lock | 70 +++++++++---------- pubspec.yaml | 2 +- 23 files changed, 84 insertions(+), 80 deletions(-) diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml index d3f747a..16e7b7c 100644 --- a/.github/workflows/test_env_setup_dev.yaml +++ b/.github/workflows/test_env_setup_dev.yaml @@ -51,14 +51,27 @@ jobs: echo "API_BASE=$API_BASE" >> $GITHUB_ENV echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV - - name: Create .env file + - name: Validate .env run: | - echo "API_COURSES=https://$CDN_SUBJECTS/courses.json" >> .env - echo "API_SUBJECTS=https://$CDN_SUBJECTS/allSubjects.json" >> .env - echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env - echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env - echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env - echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" >> .env - - - name: Print .env - run: cat .env \ No newline at end of file + missing=() + + check_var() { + local key=$1 + local value=$2 + if [ -z "$value" ]; then + missing+=("$key") + fi + } + + check_var "API_COURSES" "$API_COURSES" + check_var "API_SUBJECTS" "$API_SUBJECTS" + check_var "API_GRADE_OPTMIZER" "$API_GRADE_OPTMIZER" + check_var "API_GENETIC_ALGORITHM" "$API_GENETIC_ALGORITHM" + check_var "API_SUPPORT_URL" "$API_SUPPORT_URL" + check_var "API_FINAL_SCORE_URL" "$API_FINAL_SCORE_URL" + + if [ ${#missing[@]} -gt 0 ]; then + echo "❌ Missing environment variables:" + printf ' - %s\n' "${missing[@]}" + exit 1 + fi \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 8f4efc8..2e683c6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -57,7 +57,7 @@ android { defaultConfig { applicationId "com.dev_medias_front_rn_v2" - minSdkVersion 21 + minSdkVersion flutter.minSdkVersion targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/gradle.properties b/android/gradle.properties index 598d13f..f018a61 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx4G +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e1ca574..afa1e8e 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 1d6d19b..bd247b0 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "com.android.application" version "8.7.0" apply false + id "org.jetbrains.kotlin.android" version "2.1.0" apply false } include ":app" diff --git a/lib/app/controller/common/common_controller.dart b/lib/app/controller/common/common_controller.dart index 493942d..fa82f22 100644 --- a/lib/app/controller/common/common_controller.dart +++ b/lib/app/controller/common/common_controller.dart @@ -2,9 +2,9 @@ import 'package:mobx/mobx.dart'; part 'common_controller.g.dart'; -class CommonController = _CommonControllerBase with _$CommonController; +class CommonController = CommonControllerBase with _$CommonController; -abstract class _CommonControllerBase with Store { +abstract class CommonControllerBase with Store { @observable String? previousPage; diff --git a/lib/app/controller/common/common_controller.g.dart b/lib/app/controller/common/common_controller.g.dart index 5d7835b..1b6415c 100644 --- a/lib/app/controller/common/common_controller.g.dart +++ b/lib/app/controller/common/common_controller.g.dart @@ -8,7 +8,7 @@ part of 'common_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers -mixin _$CommonController on _CommonControllerBase, Store { +mixin _$CommonController on CommonControllerBase, Store { Computed? _$getPreviousPageComputed; @override diff --git a/lib/app/controller/common/graduations_controller.dart b/lib/app/controller/common/graduations_controller.dart index 66a9ca0..3310b98 100644 --- a/lib/app/controller/common/graduations_controller.dart +++ b/lib/app/controller/common/graduations_controller.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:dev_medias_front_flutter/app/service/graduation_service.dart'; import 'package:dio/dio.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:mobx/mobx.dart'; part 'graduations_controller.g.dart'; diff --git a/lib/app/controller/common/support_controller.dart b/lib/app/controller/common/support_controller.dart index 7fbad83..054b448 100644 --- a/lib/app/controller/common/support_controller.dart +++ b/lib/app/controller/common/support_controller.dart @@ -4,9 +4,9 @@ import 'package:mobx/mobx.dart'; part 'support_controller.g.dart'; -class SupportController = _SupportControllerBase with _$SupportController; +class SupportController = SupportControllerBase with _$SupportController; -abstract class _SupportControllerBase with Store { +abstract class SupportControllerBase with Store { SupportService supportService = SupportService(); diff --git a/lib/app/controller/common/support_controller.g.dart b/lib/app/controller/common/support_controller.g.dart index c09e21e..17f3523 100644 --- a/lib/app/controller/common/support_controller.g.dart +++ b/lib/app/controller/common/support_controller.g.dart @@ -8,7 +8,7 @@ part of 'support_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers -mixin _$SupportController on _SupportControllerBase, Store { +mixin _$SupportController on SupportControllerBase, Store { Computed? _$getEmailControllerComputed; @override diff --git a/lib/app/controller/common/user_controller.dart b/lib/app/controller/common/user_controller.dart index a958543..5aa9c5c 100644 --- a/lib/app/controller/common/user_controller.dart +++ b/lib/app/controller/common/user_controller.dart @@ -2,9 +2,7 @@ import 'dart:async'; import 'package:dev_medias_front_flutter/app/controller/common/courses_controller.dart'; import 'package:dev_medias_front_flutter/app/model/user.dart'; import 'package:dev_medias_front_flutter/app/service/user_service.dart'; -import 'package:hive_flutter/adapters.dart'; import 'package:mobx/mobx.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'user_controller.g.dart'; class UserController = UserControllerBase with _$UserController; diff --git a/lib/app/controller/popup_delete_controller.dart b/lib/app/controller/popup_delete_controller.dart index 0493fb5..926cd2b 100644 --- a/lib/app/controller/popup_delete_controller.dart +++ b/lib/app/controller/popup_delete_controller.dart @@ -2,9 +2,9 @@ import 'package:mobx/mobx.dart'; part 'popup_delete_controller.g.dart'; -class PopupDeleteController = _PopupDeleteController with _$PopupDeleteController; +class PopupDeleteController = PopupDeleteControllerBase with _$PopupDeleteController; -abstract class _PopupDeleteController with Store { +abstract class PopupDeleteControllerBase with Store { @observable bool isVisible = false; diff --git a/lib/app/controller/popup_delete_controller.g.dart b/lib/app/controller/popup_delete_controller.g.dart index d94bff7..6c84a4e 100644 --- a/lib/app/controller/popup_delete_controller.g.dart +++ b/lib/app/controller/popup_delete_controller.g.dart @@ -8,7 +8,7 @@ part of 'popup_delete_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers -mixin _$PopupDeleteController on _PopupDeleteController, Store { +mixin _$PopupDeleteController on PopupDeleteControllerBase, Store { late final _$isVisibleAtom = Atom(name: '_PopupDeleteController.isVisible', context: context); diff --git a/lib/app/view/edit_page_view.dart b/lib/app/view/edit_page_view.dart index 418d199..77d783d 100644 --- a/lib/app/view/edit_page_view.dart +++ b/lib/app/view/edit_page_view.dart @@ -540,7 +540,7 @@ class _EditPageState extends State { backgroundColor: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.red.withOpacity(0.5) + ? AppColors.red.withValues(alpha: 0.5) : AppColors.red, shape: RoundedRectangleBorder( borderRadius: Round.primary), @@ -558,7 +558,7 @@ class _EditPageState extends State { color: widget.course.assignments! .isEmpty && widget.course.exams!.isEmpty - ? AppColors.white.withOpacity(0.5) + ? AppColors.white.withValues(alpha: 0.5) : AppColors.white, fontSize: 16.sp), ), @@ -736,8 +736,8 @@ class _EditPageState extends State { TextButton( style: TextButton.styleFrom( backgroundColor: isDisabled - ? AppColors.red.withOpacity( - 0.5) + ? AppColors.red.withValues( + alpha: 0.5) : AppColors .red, shape: RoundedRectangleBorder( @@ -774,7 +774,7 @@ class _EditPageState extends State { "Confirmar", style: TextStyle( color: isDisabled - ? AppColors.white.withOpacity(0.5) + ? AppColors.white.withValues(alpha: 0.5) : AppColors.white), )), ), @@ -788,7 +788,7 @@ class _EditPageState extends State { backgroundColor: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.red.withOpacity(0.5) + ? AppColors.red.withValues(alpha: 0.5) : AppColors.red, shape: RoundedRectangleBorder( borderRadius: Round.primary), @@ -802,7 +802,7 @@ class _EditPageState extends State { style: TextStyle( color: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.white.withOpacity(0.5) + ? AppColors.white.withValues(alpha: 0.5) : AppColors.white, fontSize: 16.sp), ), diff --git a/lib/app/view/home_page_view.dart b/lib/app/view/home_page_view.dart index 8909020..e5acfc4 100644 --- a/lib/app/view/home_page_view.dart +++ b/lib/app/view/home_page_view.dart @@ -348,7 +348,7 @@ class _HomePageState extends State { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('hasAcceptedTerms', true); - Navigator.of(context).pop(); + if (context.mounted) Navigator.of(context).pop(); }, child: const Text( "Aceitar", diff --git a/lib/app/widgets/add_course_form.dart b/lib/app/widgets/add_course_form.dart index 11788bc..f1ef812 100644 --- a/lib/app/widgets/add_course_form.dart +++ b/lib/app/widgets/add_course_form.dart @@ -137,7 +137,7 @@ class _AddCourseFormState extends State { graduationController.text, int.parse(yearController.text)); introPageController.setLoginSuccesful(true); - Navigator.pushReplacementNamed(context, '/home'); + if (context.mounted) Navigator.pushReplacementNamed(context, '/home'); } }, style: TextButton.styleFrom( diff --git a/lib/app/widgets/add_course_navigation_button.dart b/lib/app/widgets/add_course_navigation_button.dart index 7f1b17f..1c3e48f 100644 --- a/lib/app/widgets/add_course_navigation_button.dart +++ b/lib/app/widgets/add_course_navigation_button.dart @@ -1,9 +1,6 @@ -import 'package:dev_medias_front_flutter/app/animations/transitions/horizontal_push.dart'; import 'package:dev_medias_front_flutter/app/controller/common/common_controller.dart'; import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; import 'package:dev_medias_front_flutter/app/utils/theme/measurements.dart'; -import 'package:dev_medias_front_flutter/app/view/add_page_view.dart'; -import 'package:dev_medias_front_flutter/app/view/home_page_view.dart'; import 'package:flutter/material.dart'; class AddCourseNavigationButton extends StatelessWidget { diff --git a/lib/app/widgets/common/navigation_top_bar.dart b/lib/app/widgets/common/navigation_top_bar.dart index f6fac55..04a83ae 100644 --- a/lib/app/widgets/common/navigation_top_bar.dart +++ b/lib/app/widgets/common/navigation_top_bar.dart @@ -1,5 +1,4 @@ import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; -import 'package:dev_medias_front_flutter/app/view/home_page_view.dart'; import 'package:dev_medias_front_flutter/app/widgets/common/return_button.dart'; import 'package:dev_medias_front_flutter/app/widgets/popup_report_error.dart'; import 'package:flutter/material.dart'; diff --git a/lib/app/widgets/common/return_button.dart b/lib/app/widgets/common/return_button.dart index c72c095..f2a5335 100644 --- a/lib/app/widgets/common/return_button.dart +++ b/lib/app/widgets/common/return_button.dart @@ -20,8 +20,6 @@ class ReturnButton extends StatelessWidget { ), onPressed: prevPage != null ? () { - // Navegar para a tela anterior se tiver uma página anterior especificada no CommonController - String? pageToPush = prevPage; Navigator.pop(context); commonController.setPreviousPage(null); } diff --git a/lib/app/widgets/popup_delete_course.dart b/lib/app/widgets/popup_delete_course.dart index e4b46e5..3ad1be9 100644 --- a/lib/app/widgets/popup_delete_course.dart +++ b/lib/app/widgets/popup_delete_course.dart @@ -6,7 +6,7 @@ import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; class PopupDeleteCourseWidget extends StatelessWidget { final PopupDeleteController controller; - const PopupDeleteCourseWidget({required this.controller}); + const PopupDeleteCourseWidget({super.key, required this.controller}); @override Widget build(BuildContext context) { diff --git a/lib/app/widgets/starting_data_form.dart b/lib/app/widgets/starting_data_form.dart index 57736d4..53c85ef 100644 --- a/lib/app/widgets/starting_data_form.dart +++ b/lib/app/widgets/starting_data_form.dart @@ -141,7 +141,7 @@ class _StartingDataFormState extends State { graduationController.text, int.parse(yearController.text)); introPageController.setLoginSuccesful(true); - Navigator.pushReplacementNamed(context, '/home'); + if (context.mounted) Navigator.pushReplacementNamed(context, '/home'); } }, style: TextButton.styleFrom( diff --git a/pubspec.lock b/pubspec.lock index d9bf688..b57a937 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: @@ -409,10 +409,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" io: dependency: transitive description: @@ -441,26 +441,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -489,10 +489,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -505,10 +505,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" mime: dependency: transitive description: @@ -526,13 +526,13 @@ packages: source: hosted version: "2.3.3+2" mobx_codegen: - dependency: "direct main" + dependency: "direct dev" description: name: mobx_codegen - sha256: "8e0d8653a0c720ad933cd8358f6f89f740ce89203657c13f25bea772ef1fff7c" + sha256: f605e04444c5dbf8503f354577e7ad1e06887cff30ba20f147365d6130d580f7 url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.6.2" nested: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_parsing: dependency: transitive description: @@ -745,7 +745,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: @@ -766,18 +766,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: @@ -806,10 +806,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.6" timing: dependency: transitive description: @@ -854,10 +854,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -923,5 +923,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9cc7024..0fdffe3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,7 +35,6 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 mobx: ^2.3.3+2 - mobx_codegen: ^2.6.1 flutter_mobx: ^2.2.1+1 flutter_svg: ^2.0.10+1 path: ^1.9.0 @@ -57,6 +56,7 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^4.0.0 + mobx_codegen: ^2.6.1 build_runner: ^2.4.9 flutter_launcher_icons: ^0.14.1 From 700e798d5ebf3b66f5d374baa1ad7ef0883f5d39 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 19:01:06 -0300 Subject: [PATCH 10/18] t Revert "updating test env setup dev CI" This reverts commit 837ee80e1291c0046cb3303de9028178a40e1413. --- .github/workflows/test_env_setup_dev.yaml | 33 +++------ android/app/build.gradle | 2 +- android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 4 +- .../controller/common/common_controller.dart | 4 +- .../common/common_controller.g.dart | 2 +- .../common/graduations_controller.dart | 1 + .../controller/common/support_controller.dart | 4 +- .../common/support_controller.g.dart | 2 +- .../controller/common/user_controller.dart | 2 + .../controller/popup_delete_controller.dart | 4 +- .../controller/popup_delete_controller.g.dart | 2 +- lib/app/view/edit_page_view.dart | 14 ++-- lib/app/view/home_page_view.dart | 2 +- lib/app/widgets/add_course_form.dart | 2 +- .../widgets/add_course_navigation_button.dart | 3 + .../widgets/common/navigation_top_bar.dart | 1 + lib/app/widgets/common/return_button.dart | 2 + lib/app/widgets/popup_delete_course.dart | 2 +- lib/app/widgets/starting_data_form.dart | 2 +- pubspec.lock | 70 +++++++++---------- pubspec.yaml | 2 +- 23 files changed, 80 insertions(+), 84 deletions(-) diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml index 16e7b7c..d3f747a 100644 --- a/.github/workflows/test_env_setup_dev.yaml +++ b/.github/workflows/test_env_setup_dev.yaml @@ -51,27 +51,14 @@ jobs: echo "API_BASE=$API_BASE" >> $GITHUB_ENV echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV - - name: Validate .env + - name: Create .env file run: | - missing=() - - check_var() { - local key=$1 - local value=$2 - if [ -z "$value" ]; then - missing+=("$key") - fi - } - - check_var "API_COURSES" "$API_COURSES" - check_var "API_SUBJECTS" "$API_SUBJECTS" - check_var "API_GRADE_OPTMIZER" "$API_GRADE_OPTMIZER" - check_var "API_GENETIC_ALGORITHM" "$API_GENETIC_ALGORITHM" - check_var "API_SUPPORT_URL" "$API_SUPPORT_URL" - check_var "API_FINAL_SCORE_URL" "$API_FINAL_SCORE_URL" - - if [ ${#missing[@]} -gt 0 ]; then - echo "❌ Missing environment variables:" - printf ' - %s\n' "${missing[@]}" - exit 1 - fi \ No newline at end of file + echo "API_COURSES=https://$CDN_SUBJECTS/courses.json" >> .env + echo "API_SUBJECTS=https://$CDN_SUBJECTS/allSubjects.json" >> .env + echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env + echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env + echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env + echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" >> .env + + - name: Print .env + run: cat .env \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 2e683c6..8f4efc8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -57,7 +57,7 @@ android { defaultConfig { applicationId "com.dev_medias_front_rn_v2" - minSdkVersion flutter.minSdkVersion + minSdkVersion 21 targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/gradle.properties b/android/gradle.properties index f018a61..598d13f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx4G android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index afa1e8e..e1ca574 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index bd247b0..1d6d19b 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.7.0" apply false - id "org.jetbrains.kotlin.android" version "2.1.0" apply false + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false } include ":app" diff --git a/lib/app/controller/common/common_controller.dart b/lib/app/controller/common/common_controller.dart index fa82f22..493942d 100644 --- a/lib/app/controller/common/common_controller.dart +++ b/lib/app/controller/common/common_controller.dart @@ -2,9 +2,9 @@ import 'package:mobx/mobx.dart'; part 'common_controller.g.dart'; -class CommonController = CommonControllerBase with _$CommonController; +class CommonController = _CommonControllerBase with _$CommonController; -abstract class CommonControllerBase with Store { +abstract class _CommonControllerBase with Store { @observable String? previousPage; diff --git a/lib/app/controller/common/common_controller.g.dart b/lib/app/controller/common/common_controller.g.dart index 1b6415c..5d7835b 100644 --- a/lib/app/controller/common/common_controller.g.dart +++ b/lib/app/controller/common/common_controller.g.dart @@ -8,7 +8,7 @@ part of 'common_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers -mixin _$CommonController on CommonControllerBase, Store { +mixin _$CommonController on _CommonControllerBase, Store { Computed? _$getPreviousPageComputed; @override diff --git a/lib/app/controller/common/graduations_controller.dart b/lib/app/controller/common/graduations_controller.dart index 3310b98..66a9ca0 100644 --- a/lib/app/controller/common/graduations_controller.dart +++ b/lib/app/controller/common/graduations_controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:dev_medias_front_flutter/app/service/graduation_service.dart'; import 'package:dio/dio.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:mobx/mobx.dart'; part 'graduations_controller.g.dart'; diff --git a/lib/app/controller/common/support_controller.dart b/lib/app/controller/common/support_controller.dart index 054b448..7fbad83 100644 --- a/lib/app/controller/common/support_controller.dart +++ b/lib/app/controller/common/support_controller.dart @@ -4,9 +4,9 @@ import 'package:mobx/mobx.dart'; part 'support_controller.g.dart'; -class SupportController = SupportControllerBase with _$SupportController; +class SupportController = _SupportControllerBase with _$SupportController; -abstract class SupportControllerBase with Store { +abstract class _SupportControllerBase with Store { SupportService supportService = SupportService(); diff --git a/lib/app/controller/common/support_controller.g.dart b/lib/app/controller/common/support_controller.g.dart index 17f3523..c09e21e 100644 --- a/lib/app/controller/common/support_controller.g.dart +++ b/lib/app/controller/common/support_controller.g.dart @@ -8,7 +8,7 @@ part of 'support_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers -mixin _$SupportController on SupportControllerBase, Store { +mixin _$SupportController on _SupportControllerBase, Store { Computed? _$getEmailControllerComputed; @override diff --git a/lib/app/controller/common/user_controller.dart b/lib/app/controller/common/user_controller.dart index 5aa9c5c..a958543 100644 --- a/lib/app/controller/common/user_controller.dart +++ b/lib/app/controller/common/user_controller.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:dev_medias_front_flutter/app/controller/common/courses_controller.dart'; import 'package:dev_medias_front_flutter/app/model/user.dart'; import 'package:dev_medias_front_flutter/app/service/user_service.dart'; +import 'package:hive_flutter/adapters.dart'; import 'package:mobx/mobx.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'user_controller.g.dart'; class UserController = UserControllerBase with _$UserController; diff --git a/lib/app/controller/popup_delete_controller.dart b/lib/app/controller/popup_delete_controller.dart index 926cd2b..0493fb5 100644 --- a/lib/app/controller/popup_delete_controller.dart +++ b/lib/app/controller/popup_delete_controller.dart @@ -2,9 +2,9 @@ import 'package:mobx/mobx.dart'; part 'popup_delete_controller.g.dart'; -class PopupDeleteController = PopupDeleteControllerBase with _$PopupDeleteController; +class PopupDeleteController = _PopupDeleteController with _$PopupDeleteController; -abstract class PopupDeleteControllerBase with Store { +abstract class _PopupDeleteController with Store { @observable bool isVisible = false; diff --git a/lib/app/controller/popup_delete_controller.g.dart b/lib/app/controller/popup_delete_controller.g.dart index 6c84a4e..d94bff7 100644 --- a/lib/app/controller/popup_delete_controller.g.dart +++ b/lib/app/controller/popup_delete_controller.g.dart @@ -8,7 +8,7 @@ part of 'popup_delete_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers -mixin _$PopupDeleteController on PopupDeleteControllerBase, Store { +mixin _$PopupDeleteController on _PopupDeleteController, Store { late final _$isVisibleAtom = Atom(name: '_PopupDeleteController.isVisible', context: context); diff --git a/lib/app/view/edit_page_view.dart b/lib/app/view/edit_page_view.dart index 77d783d..418d199 100644 --- a/lib/app/view/edit_page_view.dart +++ b/lib/app/view/edit_page_view.dart @@ -540,7 +540,7 @@ class _EditPageState extends State { backgroundColor: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.red.withValues(alpha: 0.5) + ? AppColors.red.withOpacity(0.5) : AppColors.red, shape: RoundedRectangleBorder( borderRadius: Round.primary), @@ -558,7 +558,7 @@ class _EditPageState extends State { color: widget.course.assignments! .isEmpty && widget.course.exams!.isEmpty - ? AppColors.white.withValues(alpha: 0.5) + ? AppColors.white.withOpacity(0.5) : AppColors.white, fontSize: 16.sp), ), @@ -736,8 +736,8 @@ class _EditPageState extends State { TextButton( style: TextButton.styleFrom( backgroundColor: isDisabled - ? AppColors.red.withValues( - alpha: 0.5) + ? AppColors.red.withOpacity( + 0.5) : AppColors .red, shape: RoundedRectangleBorder( @@ -774,7 +774,7 @@ class _EditPageState extends State { "Confirmar", style: TextStyle( color: isDisabled - ? AppColors.white.withValues(alpha: 0.5) + ? AppColors.white.withOpacity(0.5) : AppColors.white), )), ), @@ -788,7 +788,7 @@ class _EditPageState extends State { backgroundColor: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.red.withValues(alpha: 0.5) + ? AppColors.red.withOpacity(0.5) : AppColors.red, shape: RoundedRectangleBorder( borderRadius: Round.primary), @@ -802,7 +802,7 @@ class _EditPageState extends State { style: TextStyle( color: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.white.withValues(alpha: 0.5) + ? AppColors.white.withOpacity(0.5) : AppColors.white, fontSize: 16.sp), ), diff --git a/lib/app/view/home_page_view.dart b/lib/app/view/home_page_view.dart index e5acfc4..8909020 100644 --- a/lib/app/view/home_page_view.dart +++ b/lib/app/view/home_page_view.dart @@ -348,7 +348,7 @@ class _HomePageState extends State { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('hasAcceptedTerms', true); - if (context.mounted) Navigator.of(context).pop(); + Navigator.of(context).pop(); }, child: const Text( "Aceitar", diff --git a/lib/app/widgets/add_course_form.dart b/lib/app/widgets/add_course_form.dart index f1ef812..11788bc 100644 --- a/lib/app/widgets/add_course_form.dart +++ b/lib/app/widgets/add_course_form.dart @@ -137,7 +137,7 @@ class _AddCourseFormState extends State { graduationController.text, int.parse(yearController.text)); introPageController.setLoginSuccesful(true); - if (context.mounted) Navigator.pushReplacementNamed(context, '/home'); + Navigator.pushReplacementNamed(context, '/home'); } }, style: TextButton.styleFrom( diff --git a/lib/app/widgets/add_course_navigation_button.dart b/lib/app/widgets/add_course_navigation_button.dart index 1c3e48f..7f1b17f 100644 --- a/lib/app/widgets/add_course_navigation_button.dart +++ b/lib/app/widgets/add_course_navigation_button.dart @@ -1,6 +1,9 @@ +import 'package:dev_medias_front_flutter/app/animations/transitions/horizontal_push.dart'; import 'package:dev_medias_front_flutter/app/controller/common/common_controller.dart'; import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; import 'package:dev_medias_front_flutter/app/utils/theme/measurements.dart'; +import 'package:dev_medias_front_flutter/app/view/add_page_view.dart'; +import 'package:dev_medias_front_flutter/app/view/home_page_view.dart'; import 'package:flutter/material.dart'; class AddCourseNavigationButton extends StatelessWidget { diff --git a/lib/app/widgets/common/navigation_top_bar.dart b/lib/app/widgets/common/navigation_top_bar.dart index 04a83ae..f6fac55 100644 --- a/lib/app/widgets/common/navigation_top_bar.dart +++ b/lib/app/widgets/common/navigation_top_bar.dart @@ -1,4 +1,5 @@ import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; +import 'package:dev_medias_front_flutter/app/view/home_page_view.dart'; import 'package:dev_medias_front_flutter/app/widgets/common/return_button.dart'; import 'package:dev_medias_front_flutter/app/widgets/popup_report_error.dart'; import 'package:flutter/material.dart'; diff --git a/lib/app/widgets/common/return_button.dart b/lib/app/widgets/common/return_button.dart index f2a5335..c72c095 100644 --- a/lib/app/widgets/common/return_button.dart +++ b/lib/app/widgets/common/return_button.dart @@ -20,6 +20,8 @@ class ReturnButton extends StatelessWidget { ), onPressed: prevPage != null ? () { + // Navegar para a tela anterior se tiver uma página anterior especificada no CommonController + String? pageToPush = prevPage; Navigator.pop(context); commonController.setPreviousPage(null); } diff --git a/lib/app/widgets/popup_delete_course.dart b/lib/app/widgets/popup_delete_course.dart index 3ad1be9..e4b46e5 100644 --- a/lib/app/widgets/popup_delete_course.dart +++ b/lib/app/widgets/popup_delete_course.dart @@ -6,7 +6,7 @@ import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; class PopupDeleteCourseWidget extends StatelessWidget { final PopupDeleteController controller; - const PopupDeleteCourseWidget({super.key, required this.controller}); + const PopupDeleteCourseWidget({required this.controller}); @override Widget build(BuildContext context) { diff --git a/lib/app/widgets/starting_data_form.dart b/lib/app/widgets/starting_data_form.dart index 53c85ef..57736d4 100644 --- a/lib/app/widgets/starting_data_form.dart +++ b/lib/app/widgets/starting_data_form.dart @@ -141,7 +141,7 @@ class _StartingDataFormState extends State { graduationController.text, int.parse(yearController.text)); introPageController.setLoginSuccesful(true); - if (context.mounted) Navigator.pushReplacementNamed(context, '/home'); + Navigator.pushReplacementNamed(context, '/home'); } }, style: TextButton.styleFrom( diff --git a/pubspec.lock b/pubspec.lock index b57a937..d9bf688 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" code_builder: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: @@ -409,10 +409,10 @@ packages: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" io: dependency: transitive description: @@ -441,26 +441,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -489,10 +489,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -505,10 +505,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mime: dependency: transitive description: @@ -526,13 +526,13 @@ packages: source: hosted version: "2.3.3+2" mobx_codegen: - dependency: "direct dev" + dependency: "direct main" description: name: mobx_codegen - sha256: f605e04444c5dbf8503f354577e7ad1e06887cff30ba20f147365d6130d580f7 + sha256: "8e0d8653a0c720ad933cd8358f6f89f740ce89203657c13f25bea772ef1fff7c" url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.6.1" nested: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -745,7 +745,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_gen: dependency: transitive description: @@ -766,18 +766,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -806,10 +806,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.2" timing: dependency: transitive description: @@ -854,10 +854,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: @@ -923,5 +923,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0fdffe3..9cc7024 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 mobx: ^2.3.3+2 + mobx_codegen: ^2.6.1 flutter_mobx: ^2.2.1+1 flutter_svg: ^2.0.10+1 path: ^1.9.0 @@ -56,7 +57,6 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^4.0.0 - mobx_codegen: ^2.6.1 build_runner: ^2.4.9 flutter_launcher_icons: ^0.14.1 From bf159c18472fe1679ba2b4431144099bbb86efd9 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 19:03:25 -0300 Subject: [PATCH 11/18] commiting only necessary changes --- .github/workflows/test_env_setup_dev.yaml | 27 +++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml index d3f747a..ef43ade 100644 --- a/.github/workflows/test_env_setup_dev.yaml +++ b/.github/workflows/test_env_setup_dev.yaml @@ -53,12 +53,21 @@ jobs: - name: Create .env file run: | - echo "API_COURSES=https://$CDN_SUBJECTS/courses.json" >> .env - echo "API_SUBJECTS=https://$CDN_SUBJECTS/allSubjects.json" >> .env - echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env - echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env - echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env - echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" >> .env - - - name: Print .env - run: cat .env \ No newline at end of file + missing=() + + check_var() { + local key=$1 + local value=$2 + if [ -z "$value" ]; then + missing+=("$key") + fi + } + + check_var "API_BASE" "$API_BASE" + check_var "CDN_SUBJECTS" "$CDN_SUBJECTS" + + if [ ${#missing[@]} -gt 0 ]; then + echo "❌ Missing environment variables:" + printf ' - %s\n' "${missing[@]}" + exit 1 + fi \ No newline at end of file From 567b299b853199b149ad626aa5319b0d8efc1170 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 26 Mar 2026 19:30:12 -0300 Subject: [PATCH 12/18] updating test env setup --- .github/workflows/test_env_setup_dev.yaml | 19 +++++-- pubspec.lock | 64 +++++++++++------------ 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml index ef43ade..183f305 100644 --- a/.github/workflows/test_env_setup_dev.yaml +++ b/.github/workflows/test_env_setup_dev.yaml @@ -42,16 +42,25 @@ jobs: API_BASE=$(aws ssm get-parameter \ --name "/devmedias/$STAGE/api/url" \ - --query "Parameter.Value" --output text) + --query "Parameter.Value" --output text) || true CDN_SUBJECTS=$(aws ssm get-parameter \ --name "/devmedias/$STAGE/cdn/subjects" \ - --query "Parameter.Value" --output text) + --query "Parameter.Value" --output text) || true + + if [ -n "$API_BASE" ]; then + echo "::add-mask::$API_BASE" + fi + + if [ -n "$CDN_SUBJECTS" ]; then + echo "::add-mask::$CDN_SUBJECTS" + fi echo "API_BASE=$API_BASE" >> $GITHUB_ENV echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV - - name: Create .env file + - name: Validate .env + shell: /usr/bin/bash -e {0} run: | missing=() @@ -70,4 +79,6 @@ jobs: echo "❌ Missing environment variables:" printf ' - %s\n' "${missing[@]}" exit 1 - fi \ No newline at end of file + fi + + echo "✅ All environment variables are set" \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index d9bf688..4333e77 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: @@ -409,10 +409,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" io: dependency: transitive description: @@ -441,26 +441,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -489,10 +489,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -505,10 +505,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" mime: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_parsing: dependency: transitive description: @@ -745,7 +745,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: @@ -766,18 +766,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: @@ -806,10 +806,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.6" timing: dependency: transitive description: @@ -854,10 +854,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -923,5 +923,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.22.0" From 150784fde381b5335170ce07e46ef864ea5f7183 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Sat, 28 Mar 2026 08:14:59 -0300 Subject: [PATCH 13/18] updated packages, old imports and broken parts --- android/app/build.gradle | 12 +- android/build.gradle | 7 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 4 +- lib/app/view/edit_page_view.dart | 14 +- pubspec.lock | 364 ++++++++---------- pubspec.yaml | 24 +- 7 files changed, 196 insertions(+), 231 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8f4efc8..f5ae664 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -43,21 +43,17 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + jvmTarget = '17' } defaultConfig { applicationId "com.dev_medias_front_rn_v2" - minSdkVersion 21 + minSdkVersion flutter.minSdkVersion targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/build.gradle b/android/build.gradle index bc157bd..74d873c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,14 +5,15 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.layout.buildDirectory.value(rootProject.layout.buildDirectory.dir("../build").get()) + subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" + project.layout.buildDirectory.value(rootProject.layout.buildDirectory.dir(project.name).get()) } subprojects { project.evaluationDependsOn(':app') } tasks.register("clean", Delete) { - delete rootProject.buildDir + delete rootProject.layout.buildDirectory } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e1ca574..afa1e8e 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 1d6d19b..bd247b0 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "com.android.application" version "8.7.0" apply false + id "org.jetbrains.kotlin.android" version "2.1.0" apply false } include ":app" diff --git a/lib/app/view/edit_page_view.dart b/lib/app/view/edit_page_view.dart index 418d199..77d783d 100644 --- a/lib/app/view/edit_page_view.dart +++ b/lib/app/view/edit_page_view.dart @@ -540,7 +540,7 @@ class _EditPageState extends State { backgroundColor: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.red.withOpacity(0.5) + ? AppColors.red.withValues(alpha: 0.5) : AppColors.red, shape: RoundedRectangleBorder( borderRadius: Round.primary), @@ -558,7 +558,7 @@ class _EditPageState extends State { color: widget.course.assignments! .isEmpty && widget.course.exams!.isEmpty - ? AppColors.white.withOpacity(0.5) + ? AppColors.white.withValues(alpha: 0.5) : AppColors.white, fontSize: 16.sp), ), @@ -736,8 +736,8 @@ class _EditPageState extends State { TextButton( style: TextButton.styleFrom( backgroundColor: isDisabled - ? AppColors.red.withOpacity( - 0.5) + ? AppColors.red.withValues( + alpha: 0.5) : AppColors .red, shape: RoundedRectangleBorder( @@ -774,7 +774,7 @@ class _EditPageState extends State { "Confirmar", style: TextStyle( color: isDisabled - ? AppColors.white.withOpacity(0.5) + ? AppColors.white.withValues(alpha: 0.5) : AppColors.white), )), ), @@ -788,7 +788,7 @@ class _EditPageState extends State { backgroundColor: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.red.withOpacity(0.5) + ? AppColors.red.withValues(alpha: 0.5) : AppColors.red, shape: RoundedRectangleBorder( borderRadius: Round.primary), @@ -802,7 +802,7 @@ class _EditPageState extends State { style: TextStyle( color: widget.course.assignments!.isEmpty && widget.course.exams!.isEmpty - ? AppColors.white.withOpacity(0.5) + ? AppColors.white.withValues(alpha: 0.5) : AppColors.white, fontSize: 16.sp), ), diff --git a/pubspec.lock b/pubspec.lock index 4333e77..87c9395 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,98 +5,82 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "93.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "10.0.1" archive: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.9" args: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.1" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" build: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "4.0.5" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.3.0" build_daemon: dependency: transitive description: name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 url: "https://pub.dev" source: hosted - version: "4.0.2" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" + version: "4.1.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" + sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" url: "https://pub.dev" source: hosted - version: "2.4.11" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe - url: "https://pub.dev" - source: hosted - version: "7.3.1" + version: "2.13.1" built_collection: dependency: transitive description: @@ -109,10 +93,10 @@ packages: dependency: transitive description: name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af" url: "https://pub.dev" source: hosted - version: "8.9.2" + version: "8.12.5" characters: dependency: transitive description: @@ -125,18 +109,18 @@ packages: dependency: transitive description: name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "2.0.4" cli_util: dependency: transitive description: name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "0.4.2" clock: dependency: transitive description: @@ -149,10 +133,10 @@ packages: dependency: transitive description: name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.11.1" collection: dependency: transitive description: @@ -165,66 +149,66 @@ packages: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" crypto: dependency: transitive description: name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.7" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" dart_style: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "15a7db352c8fc6a4d2bc475ba901c25b39fe7157541da4c16eacce6f8be83e49" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "3.1.5" device_frame: dependency: transitive description: name: device_frame - sha256: d031a06f5d6f4750009672db98a5aa1536aa4a231713852469ce394779a23d75 + sha256: "7b2ebb2a09d6cc0f086b51bd1412d7be83e0170056a7290349169be41164c86a" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.0" device_preview: dependency: "direct main" description: name: device_preview - sha256: a694acdd3894b4c7d600f4ee413afc4ff917f76026b97ab06575fe886429ef19 + sha256: "88aa1cc73ee9a8ec771b309dcbc4000cc66b5d8456b825980997640ab1195bf5" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" dio: dependency: "direct main" description: name: dio - sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0" + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c url: "https://pub.dev" source: hosted - version: "5.6.0" + version: "5.9.2" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.2" fake_async: dependency: transitive description: @@ -237,26 +221,26 @@ packages: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -266,26 +250,26 @@ packages: dependency: "direct main" description: name: flutter_dotenv - sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" + sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.2.1" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77" + sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" url: "https://pub.dev" source: hosted - version: "0.14.1" + version: "0.14.4" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" flutter_localizations: dependency: transitive description: flutter @@ -295,10 +279,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" + sha256: ba5e93467866a2991259dc51cffd41ef45f695c667c2b8e7b087bf24118b50fe url: "https://pub.dev" source: hosted - version: "2.2.1+1" + version: "2.3.0" flutter_screenutil: dependency: "direct main" description: @@ -311,10 +295,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9" url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "2.2.4" flutter_test: dependency: "direct dev" description: flutter @@ -329,26 +313,18 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" url: "https://pub.dev" source: hosted - version: "2.4.4" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" + version: "3.1.0" glob: dependency: transitive description: name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" graphs: dependency: transitive description: @@ -377,34 +353,34 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.6.0" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" image: dependency: transitive description: name: image - sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.8.0" intl: dependency: transitive description: @@ -417,26 +393,18 @@ packages: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "1.0.5" json_annotation: dependency: transitive description: name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 url: "https://pub.dev" source: hosted - version: "4.9.0" + version: "4.11.0" leak_tracker: dependency: transitive description: @@ -465,18 +433,18 @@ packages: dependency: transitive description: name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.1.1" logging: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" lucide_icons: dependency: "direct main" description: @@ -513,26 +481,26 @@ packages: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "2.0.0" mobx: dependency: "direct main" description: name: mobx - sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" + sha256: "25ea9a3ee69243d578fb6288721d8eeb18550d0391c32f896d377d994154bff2" url: "https://pub.dev" source: hosted - version: "2.3.3+2" + version: "2.6.0" mobx_codegen: dependency: "direct main" description: name: mobx_codegen - sha256: "8e0d8653a0c720ad933cd8358f6f89f740ce89203657c13f25bea772ef1fff7c" + sha256: "6529bcae074838004b58ad01fbe4966be182a176aacfb723686985986ebac19f" url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.7.7" nested: dependency: transitive description: @@ -545,10 +513,10 @@ packages: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" path: dependency: "direct main" description: @@ -561,34 +529,34 @@ packages: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" path_provider: dependency: transitive description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" + sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba" url: "https://pub.dev" source: hosted - version: "2.2.10" + version: "2.2.23" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.1" path_provider_linux: dependency: transitive description: @@ -617,18 +585,18 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "7.0.2" platform: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -641,58 +609,66 @@ packages: dependency: transitive description: name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.5.2" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.dev" + source: hosted + version: "6.5.0" provider: dependency: transitive description: name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.5+1" pub_semver: dependency: transitive description: name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.5.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: a7e8467e9181cef109f601e3f65765685786c1a738a83d7fbbde377589c0d974 + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.23" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "2.5.6" shared_preferences_linux: dependency: transitive description: @@ -705,18 +681,18 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: @@ -729,18 +705,18 @@ packages: dependency: transitive description: name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" sky_engine: dependency: transitive description: flutter @@ -750,18 +726,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "4.2.2" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.2" stack_trace: dependency: transitive description: @@ -782,26 +758,26 @@ packages: dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: @@ -810,46 +786,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.6" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.20" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.13" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.2.0" vector_math: dependency: transitive description: @@ -862,66 +830,66 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.1" web: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.1" web_socket: dependency: transitive description: name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "0.1.6" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.1.0" xml: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" yaml: dependency: transitive description: name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" sdks: - dart: ">=3.8.0-0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9cc7024..41c18a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,19 +33,19 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.6 - mobx: ^2.3.3+2 - mobx_codegen: ^2.6.1 - flutter_mobx: ^2.2.1+1 - flutter_svg: ^2.0.10+1 + cupertino_icons: ^1.0.8 + mobx: ^2.6.0 + mobx_codegen: ^2.7.6 + flutter_mobx: ^2.3.0 + flutter_svg: ^2.2.4 path: ^1.9.0 - shared_preferences: ^2.3.2 - dio: ^5.6.0 + shared_preferences: ^2.5.5 + dio: ^5.9.2 hive_flutter: ^1.1.0 - flutter_dotenv: ^5.1.0 + flutter_dotenv: ^5.2.1 lucide_icons: ^0.257.0 flutter_screenutil: ^5.9.3 - device_preview: ^1.2.0 + device_preview: ^1.3.1 dev_dependencies: flutter_test: @@ -56,9 +56,9 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^4.0.0 - build_runner: ^2.4.9 - flutter_launcher_icons: ^0.14.1 + flutter_lints: ^5.0.0 + build_runner: ^2.4.13 + flutter_launcher_icons: ^0.14.4 flutter_icons: android: true From 966aef62c021936ea13e4525218a9d7216390219 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Wed, 8 Apr 2026 16:50:06 -0300 Subject: [PATCH 14/18] extending to branch selection in ssm step, updating gitignores, updating packages --- .github/workflows/build_android.yml | 6 +----- .gitignore | 7 +++++++ android/.gitignore | 2 ++ pubspec.lock | 16 ++++++++-------- pubspec.yaml | 4 ++-- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 7a7b26d..ff0cd44 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -6,8 +6,6 @@ on: - homolog - prod - workflow_call: - jobs: build_android: environment: ${{ github.ref_name }} @@ -89,7 +87,7 @@ jobs: - name: Fetch config from SSM run: | - STAGE=dev + STAGE=$(echo "${{ github.ref_name }}" | tr '[:upper:]' '[:lower:]') API_BASE=$(aws ssm get-parameter \ --name "/devmedias/$STAGE/api/url" \ @@ -135,8 +133,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - repository: ${{ inputs.repository }} - name: Download artifact uses: actions/download-artifact@v4 diff --git a/.gitignore b/.gitignore index d40954c..9be5be1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ # Miscellaneous *.class *.log +# Lock files (do not ignore Dart/Flutter lockfile — commit pubspec.lock) *.lock +!pubspec.lock *.pyc *.swp .DS_Store @@ -35,6 +37,11 @@ migrate_working_dir/ .pub/ /build/ +# Platform build outputs (Gradle, CMake, etc.) +/android/build/ +/android/app/build/ +/ios/build/ + # Symbolication related app.*.symbols diff --git a/android/.gitignore b/android/.gitignore index 6f56801..e67dffd 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,5 +1,7 @@ gradle-wrapper.jar /.gradle +/build/ +app/build/ /captures/ /gradlew /gradlew.bat diff --git a/pubspec.lock b/pubspec.lock index 87c9395..ad22679 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -250,10 +250,10 @@ packages: dependency: "direct main" description: name: flutter_dotenv - sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b + sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4 url: "https://pub.dev" source: hosted - version: "5.2.1" + version: "6.0.0" flutter_launcher_icons: dependency: "direct dev" description: @@ -266,10 +266,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.0.0" flutter_localizations: dependency: transitive description: flutter @@ -433,10 +433,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "6.1.0" logging: dependency: transitive description: @@ -798,10 +798,10 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1" + sha256: "81da85e9ca8885ade47f9685b953cb098970d11be4821ac765580a6607ea4373" url: "https://pub.dev" source: hosted - version: "1.1.20" + version: "1.1.21" vector_graphics_codec: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 41c18a7..38f4634 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: shared_preferences: ^2.5.5 dio: ^5.9.2 hive_flutter: ^1.1.0 - flutter_dotenv: ^5.2.1 + flutter_dotenv: ^6.0.0 lucide_icons: ^0.257.0 flutter_screenutil: ^5.9.3 device_preview: ^1.3.1 @@ -56,7 +56,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^5.0.0 + flutter_lints: ^6.0.0 build_runner: ^2.4.13 flutter_launcher_icons: ^0.14.4 From 97c18cff46b165956e41b54eca6960afbc53c6d1 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Tue, 14 Apr 2026 16:54:01 -0300 Subject: [PATCH 15/18] started working on new card notice card, updated crucial versions --- .env.example | 3 +- android/app/build.gradle | 4 + .../common/courses_controller.g.dart | 11 ++ .../common/notifications_controller.dart | 26 +++ .../common/notifications_controller.g.dart | 44 +++++ .../controller/common/user_controller.g.dart | 10 + lib/app/model/notice.dart | 25 +++ lib/app/service/notification_service.dart | 50 +++++ lib/app/view/home_page_view.dart | 59 +++++- lib/app/widgets/notice_card.dart | 182 ++++++++++++++++++ 10 files changed, 409 insertions(+), 5 deletions(-) create mode 100644 lib/app/controller/common/notifications_controller.dart create mode 100644 lib/app/controller/common/notifications_controller.g.dart create mode 100644 lib/app/model/notice.dart create mode 100644 lib/app/service/notification_service.dart create mode 100644 lib/app/widgets/notice_card.dart diff --git a/.env.example b/.env.example index 4665d16..0d270db 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,5 @@ API_SUBJECTS= API_GRADE_OPTMIZER= API_GENETIC_ALGORITHM= API_SUPPORT_URL= -API_FINAL_SCORE_URL= \ No newline at end of file +API_FINAL_SCORE_URL= +API_NOTIFICATIONS= \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index f5ae664..d53381e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -51,6 +51,10 @@ android { jvmTarget = '17' } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + defaultConfig { applicationId "com.dev_medias_front_rn_v2" minSdkVersion flutter.minSdkVersion diff --git a/lib/app/controller/common/courses_controller.g.dart b/lib/app/controller/common/courses_controller.g.dart index a060a7e..719e688 100644 --- a/lib/app/controller/common/courses_controller.g.dart +++ b/lib/app/controller/common/courses_controller.g.dart @@ -89,6 +89,17 @@ mixin _$CoursesController on CoursesControllerBase, Store { } } + @override + void deleteAllCurrentCourses() { + final _$actionInfo = _$CoursesControllerBaseActionController.startAction( + name: 'CoursesControllerBase.deleteAllCurrentCourses'); + try { + return super.deleteAllCurrentCourses(); + } finally { + _$CoursesControllerBaseActionController.endAction(_$actionInfo); + } + } + @override String toString() { return ''' diff --git a/lib/app/controller/common/notifications_controller.dart b/lib/app/controller/common/notifications_controller.dart new file mode 100644 index 0000000..3dc27fd --- /dev/null +++ b/lib/app/controller/common/notifications_controller.dart @@ -0,0 +1,26 @@ +import 'package:dev_medias_front_flutter/app/model/notice.dart'; +import 'package:dev_medias_front_flutter/app/service/notification_service.dart'; +import 'package:mobx/mobx.dart'; + +part 'notifications_controller.g.dart'; + +class NotificationsController = NotificationsControllerBase + with _$NotificationsController; + +abstract class NotificationsControllerBase with Store { + NotificationsControllerBase(); + + final NotificationService _service = NotificationService(); + + @observable + ObservableList notices = ObservableList(); + + @action + Future fetchNotifications() async { + final list = await _service.getNotifications(); + notices = ObservableList.of(list); + } +} + +final NotificationsController notificationsController = + NotificationsController(); diff --git a/lib/app/controller/common/notifications_controller.g.dart b/lib/app/controller/common/notifications_controller.g.dart new file mode 100644 index 0000000..bf434e9 --- /dev/null +++ b/lib/app/controller/common/notifications_controller.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'notifications_controller.dart'; + +// ************************************************************************** +// StoreGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers + +mixin _$NotificationsController on NotificationsControllerBase, Store { + late final _$noticesAtom = + Atom(name: 'NotificationsControllerBase.notices', context: context); + + @override + ObservableList get notices { + _$noticesAtom.reportRead(); + return super.notices; + } + + @override + set notices(ObservableList value) { + _$noticesAtom.reportWrite(value, super.notices, () { + super.notices = value; + }); + } + + late final _$fetchNotificationsAsyncAction = AsyncAction( + 'NotificationsControllerBase.fetchNotifications', + context: context); + + @override + Future fetchNotifications() { + return _$fetchNotificationsAsyncAction + .run(() => super.fetchNotifications()); + } + + @override + String toString() { + return ''' +notices: ${notices} + '''; + } +} diff --git a/lib/app/controller/common/user_controller.g.dart b/lib/app/controller/common/user_controller.g.dart index 645db6f..2d299b8 100644 --- a/lib/app/controller/common/user_controller.g.dart +++ b/lib/app/controller/common/user_controller.g.dart @@ -207,6 +207,16 @@ mixin _$UserController on UserControllerBase, Store { .run(() => super.removeCurrentCourse(code)); } + late final _$deleteAllCurrentCoursesAsyncAction = AsyncAction( + 'UserControllerBase.deleteAllCurrentCourses', + context: context); + + @override + Future deleteAllCurrentCourses() { + return _$deleteAllCurrentCoursesAsyncAction + .run(() => super.deleteAllCurrentCourses()); + } + late final _$loadInitialCoursesAsyncAction = AsyncAction('UserControllerBase.loadInitialCourses', context: context); diff --git a/lib/app/model/notice.dart b/lib/app/model/notice.dart new file mode 100644 index 0000000..ddee8ca --- /dev/null +++ b/lib/app/model/notice.dart @@ -0,0 +1,25 @@ +class NoticeModel { + NoticeModel({ + required this.title, + required this.body, + }); + + final String title; + final String body; + + factory NoticeModel.fromJson(Map json) { + final title = (json['title'] ?? + json['subject'] ?? + json['heading'] ?? + 'Aviso') + .toString(); + final body = (json['description'] ?? + json['body'] ?? + json['message'] ?? + json['text'] ?? + json['content'] ?? + '') + .toString(); + return NoticeModel(title: title, body: body); + } +} diff --git a/lib/app/service/notification_service.dart b/lib/app/service/notification_service.dart new file mode 100644 index 0000000..4167510 --- /dev/null +++ b/lib/app/service/notification_service.dart @@ -0,0 +1,50 @@ +import 'package:dev_medias_front_flutter/app/model/notice.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +class NotificationService { + final Dio dio = Dio(); + + /// GET [API_NOTIFICATIONS]. Sem URL no `.env`, retorna lista vazia (sem requisição). + Future> getNotifications() async { + final url = dotenv.env['API_NOTIFICATIONS']; + if (url == null || url.trim().isEmpty) { + return []; + } + try { + final response = await dio.get(url); + if (response.statusCode != 200) { + return []; + } + return _parseList(response.data); + } catch (_) { + return []; + } + } + + List _parseList(dynamic data) { + if (data is List) { + return data + .whereType() + .map((e) => NoticeModel.fromJson(Map.from(e))) + .where((n) => n.body.trim().isNotEmpty || n.title.trim().isNotEmpty) + .toList(); + } + if (data is Map) { + final nested = data['notifications'] ?? data['data'] ?? data['items']; + if (nested is List) { + return _parseList(nested); + } + try { + final single = NoticeModel.fromJson(Map.from(data)); + if (single.body.trim().isEmpty && single.title == 'Aviso') { + return []; + } + return [single]; + } catch (_) { + return []; + } + } + return []; + } +} diff --git a/lib/app/view/home_page_view.dart b/lib/app/view/home_page_view.dart index 8909020..b422b33 100644 --- a/lib/app/view/home_page_view.dart +++ b/lib/app/view/home_page_view.dart @@ -4,13 +4,16 @@ import 'package:dev_medias_front_flutter/app/controller/common/courses_controlle import 'package:dev_medias_front_flutter/app/controller/edit_page_controller.dart'; import 'package:dev_medias_front_flutter/app/controller/home_page_controller.dart'; import 'package:dev_medias_front_flutter/app/controller/common/user_controller.dart'; +import 'package:dev_medias_front_flutter/app/controller/common/notifications_controller.dart'; import 'package:dev_medias_front_flutter/app/model/course.dart'; import 'package:dev_medias_front_flutter/app/utils/theme/measurements.dart'; import 'package:dev_medias_front_flutter/app/widgets/add_course_navigation_button.dart'; import 'package:dev_medias_front_flutter/app/widgets/current_course_card.dart'; import 'package:dev_medias_front_flutter/app/widgets/common/navigation_top_bar.dart'; import 'package:dev_medias_front_flutter/app/widgets/common/app_drawer.dart'; +import 'package:dev_medias_front_flutter/app/widgets/notice_card.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; @@ -311,14 +314,56 @@ class _HomePageState extends State { if (!hasAcceptedTerms) { _showTermsOfServiceDialog(); + } else { + _scheduleNotificationsDialog(); } } + void _scheduleNotificationsDialog() { + WidgetsBinding.instance.addPostFrameCallback((_) async { + if (!mounted) return; + await _loadAndShowNotificationsIfAny(context); + }); + } + + Future _loadAndShowNotificationsIfAny(BuildContext pageContext) async { + final notificationsUrl = dotenv.env['API_NOTIFICATIONS']; + final notificationsConfigured = notificationsUrl != null && + notificationsUrl.trim().isNotEmpty; + + if (notificationsConfigured) { + await notificationsController.fetchNotifications(); + } + + if (!pageContext.mounted) return; + + // Sem URL no .env: mostra o aviso estático (até você apontar API_NOTIFICATIONS). + if (!notificationsConfigured) { + showBlockingNoticeDialog(pageContext); + return; + } + + final items = notificationsController.notices; + if (items.isEmpty) return; + final title = items.length == 1 ? items.first.title : 'Avisos'; + final description = items.length == 1 + ? items.first.body + : items + .map((n) => '${n.title}\n\n${n.body}') + .join('\n\n────────────\n\n'); + showBlockingNoticeDialog( + pageContext, + title: title, + description: description, + ); + } + void _showTermsOfServiceDialog() { + final pageContext = context; showDialog( context: context, barrierDismissible: false, - builder: (BuildContext context) { + builder: (BuildContext dialogContext) { return AlertDialog( title: const Text( 'Termos de Serviço', @@ -345,10 +390,16 @@ class _HomePageState extends State { fixedSize: const Size(150, 50), ), onPressed: () async { - SharedPreferences prefs = - await SharedPreferences.getInstance(); + final nav = Navigator.of(dialogContext); + final prefs = await SharedPreferences.getInstance(); + if (!mounted) return; await prefs.setBool('hasAcceptedTerms', true); - Navigator.of(context).pop(); + if (!dialogContext.mounted) return; + nav.pop(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + if (!mounted) return; + await _loadAndShowNotificationsIfAny(pageContext); + }); }, child: const Text( "Aceitar", diff --git a/lib/app/widgets/notice_card.dart b/lib/app/widgets/notice_card.dart new file mode 100644 index 0000000..1b44062 --- /dev/null +++ b/lib/app/widgets/notice_card.dart @@ -0,0 +1,182 @@ +import 'package:dev_medias_front_flutter/app/utils/theme/app_colors.dart'; +import 'package:flutter/material.dart'; + +const String _kDefaultNoticeTitle = 'Aviso'; + +const String _kDefaultNoticeBody = ''' +Este é um aviso estático de exemplo. Quando a integração com a API estiver pronta, o título e o texto virão do servidor. + +A Dev Community Mauá pode publicar avisos sobre manutenção, mudanças no cálculo de médias ou novidades do aplicativo. Leia com atenção antes de continuar. + +Lembre-se de conferir suas notas oficiais no sistema da instituição. Este aplicativo é uma ferramenta de apoio e pode conter erros ou interpretações diferentes do regulamento vigente. + +Ao usar o app, você concorda em manter seus dados de cadastro atualizados e em não compartilhar credenciais de acesso com terceiros. + +Se encontrar inconsistências, utilize o canal de suporte para reportar o problema com o máximo de detalhes possível. + +Obrigado por utilizar o DevMédias. Role até o final deste texto para habilitar o botão de confirmação. +'''; + +/// Mostra o card de aviso em tela quase cheia; só fecha após rolar até o fim e tocar em ENTENDI. +Future showBlockingNoticeDialog( + BuildContext context, { + String? title, + String? description, +}) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (dialogContext) { + final size = MediaQuery.sizeOf(dialogContext); + final horizontalInset = 20.0; + final verticalInset = 32.0; + return Dialog( + insetPadding: EdgeInsets.symmetric( + horizontal: horizontalInset, + vertical: verticalInset, + ), + backgroundColor: Colors.transparent, + elevation: 0, + child: SizedBox( + width: size.width - horizontalInset * 2, + height: size.height - verticalInset * 2, + child: NoticeCard( + title: title ?? _kDefaultNoticeTitle, + description: description ?? _kDefaultNoticeBody, + ), + ), + ); + }, + ); +} + +class NoticeCard extends StatefulWidget { + const NoticeCard({ + super.key, + required this.title, + required this.description, + }); + + final String title; + final String description; + + @override + State createState() => _NoticeCardState(); +} + +class _NoticeCardState extends State { + final ScrollController _scrollController = ScrollController(); + bool _reachedBottom = false; + + static const double _scrollEndTolerance = 12; + + @override + void initState() { + super.initState(); + _scrollController.addListener(_onScroll); + WidgetsBinding.instance.addPostFrameCallback((_) => _syncScrollState()); + } + + @override + void dispose() { + _scrollController.removeListener(_onScroll); + _scrollController.dispose(); + super.dispose(); + } + + void _onScroll() => _syncScrollState(); + + void _syncScrollState() { + if (!mounted) return; + final c = _scrollController; + if (!c.hasClients) return; + final maxExtent = c.position.maxScrollExtent; + final pixels = c.position.pixels; + final atEnd = maxExtent <= _scrollEndTolerance || + pixels >= maxExtent - _scrollEndTolerance; + if (atEnd != _reachedBottom) { + setState(() => _reachedBottom = atEnd); + } + } + + void _onEntendi() { + Navigator.of(context).pop(); + } + + @override + Widget build(BuildContext context) { + return Material( + color: AppColors.backgroundLight, + borderRadius: BorderRadius.circular(16), + clipBehavior: Clip.antiAlias, + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + widget.title, + style: const TextStyle( + color: AppColors.white, + fontSize: 22, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 16), + Expanded( + child: Scrollbar( + controller: _scrollController, + thumbVisibility: true, + child: SingleChildScrollView( + controller: _scrollController, + physics: const BouncingScrollPhysics(), + child: Text( + widget.description.trim(), + style: const TextStyle( + color: AppColors.white, + fontSize: 16, + height: 1.45, + ), + ), + ), + ), + ), + const SizedBox(height: 12), + if (!_reachedBottom) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text( + 'Role até o final para continuar', + textAlign: TextAlign.center, + style: TextStyle( + color: AppColors.white.withValues(alpha: 0.55), + fontSize: 13, + ), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.red, + disabledBackgroundColor: AppColors.textFaded, + disabledForegroundColor: AppColors.white.withValues(alpha: 0.5), + foregroundColor: AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + padding: const EdgeInsets.symmetric(vertical: 14), + ), + onPressed: _reachedBottom ? _onEntendi : null, + child: const Text( + 'ENTENDI', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + ); + } +} From b3bd15fb69038ccd208ffea4119b727c4b4d09c4 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Sat, 23 May 2026 10:04:17 -0300 Subject: [PATCH 16/18] added huge changes to versions, endpoints and content persistency. New dynamo table implemented on backend, new satic notice card implmented to home page. Changes to project structure as packaged were upgraded. QOL improvment --- .env.example | 3 +- .github/workflows/build_IOS.yml | 1 - .github/workflows/build_android.yml | 56 +-- .github/workflows/test_env_setup_dev.yaml | 84 ---- android/Gemfile.lock | 2 +- .../MainActivity.kt | 0 android/build.gradle | 2 +- android/fastlane/Appfile | 2 +- lib/app/controller/add_page_controller.dart | 3 +- .../controller/common/courses_controller.dart | 16 +- .../common/courses_controller.g.dart | 9 + .../controller/common/user_controller.dart | 16 + .../controller/common/user_controller.g.dart | 10 + lib/app/controller/edit_page_controller.dart | 173 +++++-- .../controller/edit_page_controller.g.dart | 34 +- lib/app/controller/grade_controller.dart | 172 ++++--- lib/app/service/course_service.dart | 82 +++- lib/app/service/grade_api_payload.dart | 38 ++ lib/app/service/graduation_service.dart | 32 +- lib/app/service/user_service.dart | 8 + lib/app/view/edit_page_view.dart | 442 +++++++++++------- lib/app/view/home_page_view.dart | 152 +++--- lib/app/view/loading_page_view.dart | 3 +- .../widgets/common/app_lifecycle_refresh.dart | 38 ++ lib/app/widgets/notice_card.dart | 15 +- lib/main.dart | 5 +- test/grade_api_payload_test.dart | 23 + test/graduation_service_test.dart | 34 ++ 28 files changed, 920 insertions(+), 535 deletions(-) delete mode 100644 .github/workflows/test_env_setup_dev.yaml rename android/app/src/main/kotlin/com/{example/dev_medias_front_flutter => dev_medias_front_rn_v2}/MainActivity.kt (100%) create mode 100644 lib/app/service/grade_api_payload.dart create mode 100644 lib/app/widgets/common/app_lifecycle_refresh.dart create mode 100644 test/grade_api_payload_test.dart create mode 100644 test/graduation_service_test.dart diff --git a/.env.example b/.env.example index 0d270db..21d2e5c 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,6 @@ API_COURSES= API_SUBJECTS= -API_GRADE_OPTMIZER= API_GENETIC_ALGORITHM= API_SUPPORT_URL= API_FINAL_SCORE_URL= -API_NOTIFICATIONS= \ No newline at end of file +API_NOTIFICATIONS= diff --git a/.github/workflows/build_IOS.yml b/.github/workflows/build_IOS.yml index bbdbf77..72f6d31 100644 --- a/.github/workflows/build_IOS.yml +++ b/.github/workflows/build_IOS.yml @@ -28,7 +28,6 @@ # run: | # echo "GRADS_URL=${{ vars.API_COURSES }}" >> .env # The courses API # echo "COURSES_URL=${{ vars.API_SUBJECTS }}" >> .env # The subjects API - # echo "GRADE_OPTIMIZER_URL=${{ vars.API_GRADE_OPTMIZER }}" >> .env # The grade optimizer API # - name: Setup Fastlane # uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index ff0cd44..7dc359c 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest permissions: id-token: write - contents: write + contents: read steps: - name: Checkout uses: actions/checkout@v4 @@ -25,26 +25,20 @@ jobs: echo "app_build_name=$app_build_name" >> $GITHUB_ENV - name: Setup Java Version - uses: actions/setup-java@v4 # era v1 + uses: actions/setup-java@v4 with: - java-version: "17" # era 12, Flutter 3.24 exige 17+ + java-version: "17" + distribution: 'temurin' - name: Setup Flutter Version - uses: subosito/flutter-action@v2 # era v1 + uses: subosito/flutter-action@v2 with: flutter-version: "3.24.3" - name: Get flutter dependencies run: flutter pub get - - name: Setup Fastlane - uses: ruby/setup-ruby@v1 - with: - ruby-version: "2.6" - bundler-cache: true - working-directory: android - - - name: Set AWS Account ID and other variables + - name: Set AWS Account ID run: | if [[ "${{ github.ref_name }}" == "dev" ]]; then echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_DEV }}" >> $GITHUB_ENV @@ -64,9 +58,6 @@ jobs: role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/GithubActionsRole role-session-name: github-action - - name: Set envs - run: | - - name: Configure Keystore run: | echo "$ANDROID_PLAY_STORE_KEYSTORE_BASE64" | base64 --decode > app/upload-keystore.jks @@ -81,10 +72,7 @@ jobs: ANDROID_KEYSTORE_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_STORE_PASSWORD }} working-directory: android - # esse processo busca o endpoint do APIGW direto da variável setada pelo SSM configurado no back - # evitando essa passagem manual dessa endpoint pro secrets do github e evitando possíveis erros - # de passagem manual - + # Busca o endpoint do APIGW no SSM (configurado no back). - name: Fetch config from SSM run: | STAGE=$(echo "${{ github.ref_name }}" | tr '[:upper:]' '[:lower:]') @@ -93,43 +81,39 @@ jobs: --name "/devmedias/$STAGE/api/url" \ --query "Parameter.Value" --output text) - CDN_PLANS=$(aws ssm get-parameter \ - --name "/devmedias/$STAGE/cdn/plans" \ - --query "Parameter.Value" --output text) - - CDN_SUBJECTS=$(aws ssm get-parameter \ - --name "/devmedias/$STAGE/cdn/subjects" \ - --query "Parameter.Value" --output text) - + API_BASE="${API_BASE%/}/" echo "API_BASE=$API_BASE" >> $GITHUB_ENV - echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV - name: Create .env file run: | - echo "API_COURSES=https://$CDN_SUBJECTS/courses.json" >> .env - echo "API_SUBJECTS=https://$CDN_SUBJECTS/allSubjects.json" >> .env - echo "API_GRADE_OPTMIZER=${API_BASE}grade-optmizer" >> .env - echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" >> .env - echo "API_SUPPORT_URL=${API_BASE}public/contact-us" >> .env - echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" >> .env + { + echo "API_COURSES=${API_BASE}get-all-cursos" + echo "API_SUBJECTS=${API_BASE}get-all-disciplinas" + echo "API_GENETIC_ALGORITHM=${API_BASE}genetic-algorithm" + echo "API_SUPPORT_URL=${API_BASE}public/contact-us" + echo "API_FINAL_SCORE_URL=${API_BASE}calculate-mean" + echo "API_NOTIFICATIONS=" + } >> .env - name: Build Android run: | flutter build appbundle --release \ --build-name=$app_build_name \ --build-number=$app_build_number - + - name: Upload to artifacts uses: actions/upload-artifact@v4 with: name: app-release.aab path: build/app/outputs/bundle/release/ - retention-days: 1 + retention-days: 1 deploy_to_play_store: needs: build_android environment: ${{ github.ref_name }} runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/test_env_setup_dev.yaml b/.github/workflows/test_env_setup_dev.yaml deleted file mode 100644 index 183f305..0000000 --- a/.github/workflows/test_env_setup_dev.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: Test ENV Setup - -on: - push: - branches: - - dev - -jobs: - test_envs: - environment: dev - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set AWS Account ID and other variables - run: | - if [[ "${{ github.ref_name }}" == "dev" ]]; then - echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_DEV }}" >> $GITHUB_ENV - elif [[ "${{ github.ref_name }}" == "homolog" ]]; then - echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_HOML }}" >> $GITHUB_ENV - elif [[ "${{ github.ref_name }}" == "prod" ]]; then - echo "AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID_PROD }}" >> $GITHUB_ENV - else - echo "Invalid branch name!" && exit 1 - fi - echo "STACK_NAME=DevMediasStack${{github.ref_name}}" >> $GITHUB_ENV - - - name: Setup AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-region: ${{ vars.AWS_REGION }} - role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/GithubActionsRole - role-session-name: github-action - - - name: Fetch config from SSM - run: | - STAGE=dev - - API_BASE=$(aws ssm get-parameter \ - --name "/devmedias/$STAGE/api/url" \ - --query "Parameter.Value" --output text) || true - - CDN_SUBJECTS=$(aws ssm get-parameter \ - --name "/devmedias/$STAGE/cdn/subjects" \ - --query "Parameter.Value" --output text) || true - - if [ -n "$API_BASE" ]; then - echo "::add-mask::$API_BASE" - fi - - if [ -n "$CDN_SUBJECTS" ]; then - echo "::add-mask::$CDN_SUBJECTS" - fi - - echo "API_BASE=$API_BASE" >> $GITHUB_ENV - echo "CDN_SUBJECTS=$CDN_SUBJECTS" >> $GITHUB_ENV - - - name: Validate .env - shell: /usr/bin/bash -e {0} - run: | - missing=() - - check_var() { - local key=$1 - local value=$2 - if [ -z "$value" ]; then - missing+=("$key") - fi - } - - check_var "API_BASE" "$API_BASE" - check_var "CDN_SUBJECTS" "$CDN_SUBJECTS" - - if [ ${#missing[@]} -gt 0 ]; then - echo "❌ Missing environment variables:" - printf ' - %s\n' "${missing[@]}" - exit 1 - fi - - echo "✅ All environment variables are set" \ No newline at end of file diff --git a/android/Gemfile.lock b/android/Gemfile.lock index 29f7dcf..da7f77e 100644 --- a/android/Gemfile.lock +++ b/android/Gemfile.lock @@ -215,4 +215,4 @@ DEPENDENCIES fastlane BUNDLED WITH - 1.17.2 \ No newline at end of file + 1.17.2 diff --git a/android/app/src/main/kotlin/com/example/dev_medias_front_flutter/MainActivity.kt b/android/app/src/main/kotlin/com/dev_medias_front_rn_v2/MainActivity.kt similarity index 100% rename from android/app/src/main/kotlin/com/example/dev_medias_front_flutter/MainActivity.kt rename to android/app/src/main/kotlin/com/dev_medias_front_rn_v2/MainActivity.kt diff --git a/android/build.gradle b/android/build.gradle index 74d873c..690d416 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,7 +5,7 @@ allprojects { } } -rootProject.layout.buildDirectory.value(rootProject.layout.buildDirectory.dir("../build").get()) +rootProject.layout.buildDirectory.value(rootProject.layout.buildDirectory.dir("../../build").get()) subprojects { project.layout.buildDirectory.value(rootProject.layout.buildDirectory.dir(project.name).get()) diff --git a/android/fastlane/Appfile b/android/fastlane/Appfile index 3ee7e12..a507123 100644 --- a/android/fastlane/Appfile +++ b/android/fastlane/Appfile @@ -1 +1 @@ -package_name("com.dev_medias_front_rn") \ No newline at end of file +package_name("com.dev_medias_front_rn_v2") \ No newline at end of file diff --git a/lib/app/controller/add_page_controller.dart b/lib/app/controller/add_page_controller.dart index 22eeec4..d0d6386 100644 --- a/lib/app/controller/add_page_controller.dart +++ b/lib/app/controller/add_page_controller.dart @@ -20,7 +20,8 @@ abstract class AddPageControllerBase with Store { @action Future loadCourses() async { setCoursesLoaded(false); - final result = await coursesController.fetchCourses(); + await coursesController.refreshAllSubjects(); + final result = Map.from(coursesController.allCourses!); result.removeWhere( (key, value) => userController.currentCourses.contains(value.code)); availableCourses = ObservableMap.of(result); diff --git a/lib/app/controller/common/courses_controller.dart b/lib/app/controller/common/courses_controller.dart index 17a85f9..f58f8d2 100644 --- a/lib/app/controller/common/courses_controller.dart +++ b/lib/app/controller/common/courses_controller.dart @@ -57,7 +57,21 @@ abstract class CoursesControllerBase with Store { setLoadedCourses(true); return response; } catch (e) { - throw Exception('Erro de rede: $e'); + setLoadedCourses(allCourses != null && allCourses!.isNotEmpty); + rethrow; + } + } + + /// GET de todas as disciplinas + atualização do catálogo e lista da home. + @action + Future refreshAllSubjects() async { + try { + final courses = await fetchCourses(); + setAllCourses(courses); + await userController.pruneCurrentCoursesWithCatalog(); + return true; + } catch (_) { + return false; } } } diff --git a/lib/app/controller/common/courses_controller.g.dart b/lib/app/controller/common/courses_controller.g.dart index 719e688..23e4750 100644 --- a/lib/app/controller/common/courses_controller.g.dart +++ b/lib/app/controller/common/courses_controller.g.dart @@ -64,6 +64,15 @@ mixin _$CoursesController on CoursesControllerBase, Store { return _$fetchCoursesAsyncAction.run(() => super.fetchCourses()); } + late final _$refreshAllSubjectsAsyncAction = + AsyncAction('CoursesControllerBase.refreshAllSubjects', context: context); + + @override + Future refreshAllSubjects() { + return _$refreshAllSubjectsAsyncAction + .run(() => super.refreshAllSubjects()); + } + late final _$CoursesControllerBaseActionController = ActionController(name: 'CoursesControllerBase', context: context); diff --git a/lib/app/controller/common/user_controller.dart b/lib/app/controller/common/user_controller.dart index a958543..2d3fb19 100644 --- a/lib/app/controller/common/user_controller.dart +++ b/lib/app/controller/common/user_controller.dart @@ -141,6 +141,22 @@ abstract class UserControllerBase with Store { currentCourses.clear(); } + /// Remove da home códigos que não existem mais no catálogo da API. + @action + Future pruneCurrentCoursesWithCatalog() async { + final catalog = coursesController.allCourses; + if (catalog == null || catalog.isEmpty) return; + + final valid = currentCourses + .where((code) => catalog.containsKey(code)) + .toList(); + if (valid.length == currentCourses.length) return; + + final response = await service.replaceCurrentCourses(valid); + setCurrentCourses( + ObservableList.of(response['currentCourses']!.toList())); + } + @action Future loadInitialCourses(String code, int year) async { code = code.split(" ")[0]; diff --git a/lib/app/controller/common/user_controller.g.dart b/lib/app/controller/common/user_controller.g.dart index 2d299b8..b07ef47 100644 --- a/lib/app/controller/common/user_controller.g.dart +++ b/lib/app/controller/common/user_controller.g.dart @@ -217,6 +217,16 @@ mixin _$UserController on UserControllerBase, Store { .run(() => super.deleteAllCurrentCourses()); } + late final _$pruneCurrentCoursesWithCatalogAsyncAction = AsyncAction( + 'UserControllerBase.pruneCurrentCoursesWithCatalog', + context: context); + + @override + Future pruneCurrentCoursesWithCatalog() { + return _$pruneCurrentCoursesWithCatalogAsyncAction + .run(() => super.pruneCurrentCoursesWithCatalog()); + } + late final _$loadInitialCoursesAsyncAction = AsyncAction('UserControllerBase.loadInitialCourses', context: context); diff --git a/lib/app/controller/edit_page_controller.dart b/lib/app/controller/edit_page_controller.dart index 6d99bf8..8c6456c 100644 --- a/lib/app/controller/edit_page_controller.dart +++ b/lib/app/controller/edit_page_controller.dart @@ -1,4 +1,6 @@ +import 'package:dev_medias_front_flutter/app/controller/common/courses_controller.dart'; import 'package:dev_medias_front_flutter/app/controller/grade_controller.dart'; +import 'package:dev_medias_front_flutter/app/model/course.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; part 'edit_page_controller.g.dart'; @@ -29,6 +31,9 @@ abstract class EditPageControllerBase with Store { @observable bool targetCalcError = false; + @observable + bool finalScoreCalcError = false; + @observable TextEditingController finalScoreController = TextEditingController(text: ""); @@ -82,34 +87,120 @@ abstract class EditPageControllerBase with Store { targetCalcError = value; } + @action + void setFinalScoreCalcError(bool value) { + finalScoreCalcError = value; + } + + bool _needsTargetFill(String gradeName) { + return gradeControllers[gradeName]!.text.isEmpty || + gradeTypes[gradeName] != 'normal'; + } + + int _filledExamsCount(CourseModel course) { + var count = 0; + for (final exam in course.exams ?? []) { + if (!_needsTargetFill(exam.name as String)) { + count++; + } + } + return count; + } + + int _filledAssignmentsCount(CourseModel course) { + var count = 0; + for (final assignment in course.assignments ?? []) { + if (!_needsTargetFill(assignment.name as String)) { + count++; + } + } + return count; + } + // Desenha as notas meta na tela @action - void renderTargetGrades(Map grades) { + bool renderTargetGrades(Map grades) { gradeRendered = false; - if (grades.containsKey("erro")) { + targetCalcError = false; + + if (grades.containsKey('erro') || + grades['notas'] == null || + grades['notas']['provas'] == null || + grades['notas']['trabalhos'] == null) { targetCalcError = true; gradeRendered = true; targetCalcInProgress = false; - return; + return false; } - final allGrades = grades["notas"]["provas"] + grades["notas"]["trabalhos"]; - int index = 0; - // Define os valores das notas restantes como as metas recebidas - editController.grades.forEach((key, value) { - if (editController.gradeControllers[key]!.text.isEmpty || editController.gradeTypes[key] != "normal") { - editController.grades[key] = allGrades[index]["valor"]; - editController.gradeControllers[key]!.text = "${allGrades[index]["valor"]}"; - editController.gradeTypes[key] = "targetcalc"; - index++; - } - } + + final course = coursesController.allCourses?[courseCode] as CourseModel?; + if (course == null) { + targetCalcError = true; + gradeRendered = true; + targetCalcInProgress = false; + return false; + } + + final provasMeta = List>.from( + (grades['notas']['provas'] as List).map( + (item) => Map.from(item as Map), + ), ); - // Faz o mesmo para a nota final - editController.finalScoreController.text = "$targetGrade"; - editController.finalScoreGrade = targetGrade; - editController.finalScoreType = "targetcalc"; + final trabalhosMeta = List>.from( + (grades['notas']['trabalhos'] as List).map( + (item) => Map.from(item as Map), + ), + ); + + var provaQueroIndex = 0; + var trabalhoQueroIndex = 0; + final provasTenho = _filledExamsCount(course); + final trabalhosTenho = _filledAssignmentsCount(course); + + for (final exam in course.exams ?? []) { + final name = exam.name as String; + if (!_needsTargetFill(name)) { + continue; + } + final responseIndex = provasTenho + provaQueroIndex; + if (responseIndex >= provasMeta.length) { + targetCalcError = true; + gradeRendered = true; + targetCalcInProgress = false; + return false; + } + final valor = (provasMeta[responseIndex]['valor'] as num).toDouble(); + grades[name] = valor; + gradeControllers[name]!.text = '$valor'; + gradeTypes[name] = 'targetcalc'; + provaQueroIndex++; + } + + for (final assignment in course.assignments ?? []) { + final name = assignment.name as String; + if (!_needsTargetFill(name)) { + continue; + } + final responseIndex = trabalhosTenho + trabalhoQueroIndex; + if (responseIndex >= trabalhosMeta.length) { + targetCalcError = true; + gradeRendered = true; + targetCalcInProgress = false; + return false; + } + final valor = (trabalhosMeta[responseIndex]['valor'] as num).toDouble(); + grades[name] = valor; + gradeControllers[name]!.text = '$valor'; + gradeTypes[name] = 'targetcalc'; + trabalhoQueroIndex++; + } + + finalScoreController.text = '$targetGrade'; + finalScoreGrade = targetGrade; + finalScoreType = 'targetcalc'; gradeRendered = true; targetCalcInProgress = false; + return true; } @action @@ -157,26 +248,25 @@ abstract class EditPageControllerBase with Store { // Calcula as metas de nota para cada avaliação de acordo com a meta inserida com o usuário @action - Future calcTargetGrade( + Future calcTargetGrade( Map grades, Map weights) async { + setTargetCalcError(false); - // Se o tipo de uma das notas for diferente de normal, ele é considerado como zero - final filteredGrades = grades; + final filteredGrades = Map.from(grades); filteredGrades.forEach((key, value) { if (gradeTypes[key] != "normal") { filteredGrades[key] = null; } }); - // Obtém as notas meta e devolve um mapa - Map targetGrades = {}; - try { - targetGrades = await gradeController.getTargetGrades(filteredGrades, weights, targetGrade.toDouble(), courseCode); - } catch (e) { - targetGrades = {"erro": "Erro ao calcular as notas meta."}; - } - // Pega o mapa e utiliza ele para alterar os valores da tela - renderTargetGrades(targetGrades); + final targetGrades = await gradeController.getTargetGrades( + filteredGrades, + weights, + targetGrade.toDouble(), + courseCode, + ); + + return renderTargetGrades(targetGrades); } @action @@ -199,20 +289,25 @@ abstract class EditPageControllerBase with Store { // Calcula a nota final de acordo com as notas inseridas pelo usuário @action - Future calcFinalScore(Map weights, Map grades) async { + Future calcFinalScore( + Map weights, Map grades) async { + setFinalScoreCalcError(false); + final result = + await gradeController.getFinalScore(grades, weights, courseCode); - try { - Map result = await gradeController.getFinalScore(grades, weights, courseCode); - finalScoreGrade = result["media"]; - } catch (e) { + if (result.containsKey('erro')) { + finalScoreCalcError = true; finalScoreGrade = null; + finalScoreController.text = ''; + return false; } - // Atualiza o resultado final na tela - finalScoreController.text = finalScoreGrade != null ? "$finalScoreGrade" : ""; - finalScoreGrade = finalScoreGrade; - finalScoreType = "normal"; + finalScoreGrade = (result['media'] as num?)?.toDouble(); + finalScoreController.text = + finalScoreGrade != null ? '$finalScoreGrade' : ''; + finalScoreType = 'normal'; + return true; } // Formata as notas para serem enviadas ao salvamento local no Hive diff --git a/lib/app/controller/edit_page_controller.g.dart b/lib/app/controller/edit_page_controller.g.dart index 32a8279..66a04e0 100644 --- a/lib/app/controller/edit_page_controller.g.dart +++ b/lib/app/controller/edit_page_controller.g.dart @@ -122,6 +122,22 @@ mixin _$EditPageController on EditPageControllerBase, Store { }); } + late final _$finalScoreCalcErrorAtom = Atom( + name: 'EditPageControllerBase.finalScoreCalcError', context: context); + + @override + bool get finalScoreCalcError { + _$finalScoreCalcErrorAtom.reportRead(); + return super.finalScoreCalcError; + } + + @override + set finalScoreCalcError(bool value) { + _$finalScoreCalcErrorAtom.reportWrite(value, super.finalScoreCalcError, () { + super.finalScoreCalcError = value; + }); + } + late final _$finalScoreControllerAtom = Atom( name: 'EditPageControllerBase.finalScoreController', context: context); @@ -191,7 +207,7 @@ mixin _$EditPageController on EditPageControllerBase, Store { AsyncAction('EditPageControllerBase.calcTargetGrade', context: context); @override - Future calcTargetGrade( + Future calcTargetGrade( Map grades, Map weights) { return _$calcTargetGradeAsyncAction .run(() => super.calcTargetGrade(grades, weights)); @@ -201,7 +217,7 @@ mixin _$EditPageController on EditPageControllerBase, Store { AsyncAction('EditPageControllerBase.calcFinalScore', context: context); @override - Future calcFinalScore( + Future calcFinalScore( Map weights, Map grades) { return _$calcFinalScoreAsyncAction .run(() => super.calcFinalScore(weights, grades)); @@ -299,7 +315,18 @@ mixin _$EditPageController on EditPageControllerBase, Store { } @override - void renderTargetGrades(Map grades) { + void setFinalScoreCalcError(bool value) { + final _$actionInfo = _$EditPageControllerBaseActionController.startAction( + name: 'EditPageControllerBase.setFinalScoreCalcError'); + try { + return super.setFinalScoreCalcError(value); + } finally { + _$EditPageControllerBaseActionController.endAction(_$actionInfo); + } + } + + @override + bool renderTargetGrades(Map grades) { final _$actionInfo = _$EditPageControllerBaseActionController.startAction( name: 'EditPageControllerBase.renderTargetGrades'); try { @@ -374,6 +401,7 @@ courseCode: ${courseCode}, finalScoreType: ${finalScoreType}, finalScoreGrade: ${finalScoreGrade}, targetCalcError: ${targetCalcError}, +finalScoreCalcError: ${finalScoreCalcError}, finalScoreController: ${finalScoreController}, gradeTypes: ${gradeTypes}, grades: ${grades}, diff --git a/lib/app/controller/grade_controller.dart b/lib/app/controller/grade_controller.dart index ffb36d0..727b185 100644 --- a/lib/app/controller/grade_controller.dart +++ b/lib/app/controller/grade_controller.dart @@ -1,5 +1,7 @@ import 'dart:async'; import 'package:dev_medias_front_flutter/app/controller/common/courses_controller.dart'; +import 'package:dev_medias_front_flutter/app/model/course.dart'; +import 'package:dev_medias_front_flutter/app/service/grade_api_payload.dart'; import 'package:dio/dio.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:hive_flutter/adapters.dart'; @@ -13,6 +15,14 @@ abstract class GradeControllerBase with Store { final dio = Dio(); + CourseModel _course(String courseCode) { + final course = coursesController.allCourses?[courseCode]; + if (course == null) { + throw Exception('Matéria $courseCode não encontrada'); + } + return course as CourseModel; + } + @action Future getGrades(String code) async { await Hive.initFlutter(); @@ -32,81 +42,123 @@ abstract class GradeControllerBase with Store { box.put('grades', newGrades); } - @action - Future> getTargetGrades( - Map grades, - Map weights, - double targetGrade, - String courseCode) async { - Map gradeMap = { - "provas_que_tenho": [], - "trabalhos_que_tenho": [], - "provas_que_quero": [], - "trabalhos_que_quero": [], - "media_desejada": 0, - "peso_prova": coursesController.allCourses![courseCode].examWeight / 100, - "peso_trabalho": - coursesController.allCourses![courseCode].assignmentWeight / 100 + Map _buildTargetGradePayload({ + required Map grades, + required Map weights, + required double targetGrade, + required String courseCode, + }) { + final course = _course(courseCode); + final gradeMap = { + 'provas_que_tenho': >[], + 'trabalhos_que_tenho': >[], + 'provas_que_quero': >[], + 'trabalhos_que_quero': >[], + 'media_desejada': targetGrade, + 'peso_prova': normalizeCourseComponentWeight(course.examWeight), + 'peso_trabalho': normalizeCourseComponentWeight(course.assignmentWeight), }; - for (var item in grades.entries) { - if (item.key[0] == "P") { - item.value == null - ? gradeMap["provas_que_quero"].add({"peso": weights[item.key]}) - : gradeMap["provas_que_tenho"] - .add({"valor": item.value, "peso": weights[item.key]}); - } else { - item.value == null - ? gradeMap["trabalhos_que_quero"].add({"peso": weights[item.key]}) - : gradeMap["trabalhos_que_tenho"] - .add({"valor": item.value, "peso": weights[item.key]}); - } - } - gradeMap["media_desejada"] = targetGrade; - try { - final response = - await dio.post(dotenv.env['API_GRADE_OPTMIZER']!, data: gradeMap); - if (response.statusCode == 200) { - return response.data; + + for (final item in grades.entries) { + final peso = normalizeGradeWeight(weights[item.key]); + final isExam = isExamGrade(item.key, course); + if (item.value == null) { + final bucket = + isExam ? 'provas_que_quero' : 'trabalhos_que_quero'; + (gradeMap[bucket] as List).add({'peso': peso}); } else { - throw Exception('Erro na solicitação POST'); + final bucket = + isExam ? 'provas_que_tenho' : 'trabalhos_que_tenho'; + (gradeMap[bucket] as List).add({ + 'valor': (item.value as num).toDouble(), + 'peso': peso, + }); } - } catch (e) { - throw Exception('Erro de rede: $e'); } + + return gradeMap; } - @action - Future> getFinalScore(Map grades, - Map weights, String courseCode) async { - Map gradeMap = { - "provas_que_tenho": [], - "trabalhos_que_tenho": [], - "peso_prova": coursesController.allCourses![courseCode].examWeight / 100, - "peso_trabalho": - coursesController.allCourses![courseCode].assignmentWeight / 100 + Map _buildFinalScorePayload({ + required Map grades, + required Map weights, + required String courseCode, + }) { + final course = _course(courseCode); + final gradeMap = { + 'provas_que_tenho': >[], + 'trabalhos_que_tenho': >[], + 'peso_prova': normalizeCourseComponentWeight(course.examWeight), + 'peso_trabalho': normalizeCourseComponentWeight(course.assignmentWeight), }; - for (var item in grades.entries) { - if (item.key[0] == "P") { - gradeMap["provas_que_tenho"] - .add({"valor": item.value ?? 0.0, "peso": weights[item.key]}); - } else { - gradeMap["trabalhos_que_tenho"] - .add({"valor": item.value ?? 0.0, "peso": weights[item.key]}); - } + for (final item in grades.entries) { + final peso = normalizeGradeWeight(weights[item.key]); + final bucket = isExamGrade(item.key, course) + ? 'provas_que_tenho' + : 'trabalhos_que_tenho'; + (gradeMap[bucket] as List).add({ + 'valor': item.value == null ? 0.0 : (item.value as num).toDouble(), + 'peso': peso, + }); } + + return gradeMap; + } + + Future> _postJson( + String url, + Map body, + ) async { try { - final response = - await dio.post(dotenv.env['API_FINAL_SCORE_URL']!, data: gradeMap); + final response = await dio.post(url, data: body); if (response.statusCode == 200) { - return response.data; - } else { - throw Exception('Erro na solicitação POST'); + if (response.data is Map) { + return Map.from(response.data as Map); + } + final message = errorMessageFromApiBody(response.data); + return {'erro': message ?? 'Resposta inválida da API'}; } + return {'erro': 'Erro na solicitação POST (${response.statusCode})'}; + } on DioException catch (e) { + final message = errorMessageFromApiBody(e.response?.data) ?? + e.message ?? + 'Erro de rede'; + return {'erro': message}; } catch (e) { - throw Exception('Erro de rede: $e'); + return {'erro': 'Erro de rede: $e'}; } } + + @action + Future> getTargetGrades( + Map grades, + Map weights, + double targetGrade, + String courseCode, + ) async { + final gradeMap = _buildTargetGradePayload( + grades: grades, + weights: weights, + targetGrade: targetGrade, + courseCode: courseCode, + ); + return _postJson(dotenv.env['API_GENETIC_ALGORITHM']!, gradeMap); + } + + @action + Future> getFinalScore( + Map grades, + Map weights, + String courseCode, + ) async { + final gradeMap = _buildFinalScorePayload( + grades: grades, + weights: weights, + courseCode: courseCode, + ); + return _postJson(dotenv.env['API_FINAL_SCORE_URL']!, gradeMap); + } } GradeController gradeController = GradeController(); diff --git a/lib/app/service/course_service.dart b/lib/app/service/course_service.dart index 98adcec..a87ca76 100644 --- a/lib/app/service/course_service.dart +++ b/lib/app/service/course_service.dart @@ -11,30 +11,15 @@ class CourseService { try { final response = await dio.get(dotenv.env['API_SUBJECTS']!); if (response.statusCode == 200) { - Map data = response.data; - Map aux = {}; - - data.forEach((code, course) { - // converte as provas no formato GradeModel - final prevExams = course["exams"]; - final newExamList = []; - for (Map exam in prevExams) { - newExamList.add(GradeModel.fromJson(exam)); - } - course["exams"] = newExamList; - // converte os trabalhos no formato GradeModel - final prevAssignments = course["assignments"]; - final newAssignmentList = []; - for (Map assignment in prevAssignments) { - newAssignmentList.add(GradeModel.fromJson(assignment)); - } - course["assignments"] = newAssignmentList; - course = CourseModel.fromJson(course); - aux[code] = course; - }); - - Map courses = aux; - return courses; + final subjects = _parseSubjectsPayload(response.data); + final Map aux = {}; + + for (final raw in subjects) { + final course = _toCourseModel(raw); + aux[course.code] = course; + } + + return aux; } else { throw Exception('Erro na solicitação GET'); } @@ -42,6 +27,55 @@ class CourseService { throw Exception('Erro de rede: $e'); } } + + /// API nova: lista JSON; CDN antigo: mapa indexado por código. + List> _parseSubjectsPayload(dynamic data) { + if (data is List) { + return data + .map((item) => Map.from(item as Map)) + .toList(); + } + if (data is Map) { + return data.entries.map((entry) { + final subject = Map.from(entry.value as Map); + subject.putIfAbsent('code', () => entry.key.toString()); + return subject; + }).toList(); + } + throw Exception('Formato de disciplinas não reconhecido'); + } + + Map _normalizeSubjectFields(Map raw) { + return { + 'code': raw['code'], + 'name': raw['name'], + 'period': raw['period'], + 'examWeight': raw['examWeight'] ?? raw['exam_weight'], + 'assignmentWeight': raw['assignmentWeight'] ?? raw['assignment_weight'], + 'exams': raw['exams'] ?? [], + 'assignments': raw['assignments'] ?? [], + 'courses': raw['courses'], + }; + } + + CourseModel _toCourseModel(Map raw) { + final course = _normalizeSubjectFields(raw); + + final exams = []; + for (final exam in course['exams'] as List) { + exams.add(GradeModel.fromJson(Map.from(exam as Map))); + } + course['exams'] = exams; + + final assignments = []; + for (final assignment in course['assignments'] as List) { + assignments.add( + GradeModel.fromJson(Map.from(assignment as Map))); + } + course['assignments'] = assignments; + + return CourseModel.fromJson(course); + } } CourseService courseService = CourseService(); diff --git a/lib/app/service/grade_api_payload.dart b/lib/app/service/grade_api_payload.dart new file mode 100644 index 0000000..504abb5 --- /dev/null +++ b/lib/app/service/grade_api_payload.dart @@ -0,0 +1,38 @@ +import 'package:dev_medias_front_flutter/app/model/course.dart'; + +/// Converte peso da matéria (0.6 na API nova ou 60 no JSON antigo) para fração 0–1. +double normalizeCourseComponentWeight(dynamic weight) { + final value = (weight as num).toDouble(); + return value > 1 ? value / 100 : value; +} + +double normalizeGradeWeight(dynamic weight) => (weight as num).toDouble(); + +Set examNamesFor(CourseModel course) { + return (course.exams ?? []) + .map((exam) => exam.name as String) + .toSet(); +} + +Set assignmentNamesFor(CourseModel course) { + return (course.assignments ?? []) + .map((assignment) => assignment.name as String) + .toSet(); +} + +bool isExamGrade(String gradeName, CourseModel course) { + return examNamesFor(course).contains(gradeName); +} + +String? errorMessageFromApiBody(dynamic body) { + if (body is Map && body['erro'] != null) { + return body['erro'].toString(); + } + if (body is Map && body['message'] != null) { + return body['message'].toString(); + } + if (body is String) { + return body; + } + return null; +} diff --git a/lib/app/service/graduation_service.dart b/lib/app/service/graduation_service.dart index 55a7d6b..5cee58e 100644 --- a/lib/app/service/graduation_service.dart +++ b/lib/app/service/graduation_service.dart @@ -1,6 +1,35 @@ import 'package:dio/dio.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +/// API nova: lista `[{ "código": "CIC", "nome": "..." }]`. +/// CDN antigo: mapa `{ "CIC": "Ciência da Computação", ... }`. +Map parseGraduationsPayload(dynamic data) { + if (data is List) { + final result = {}; + for (final item in data) { + final course = Map.from(item as Map); + final code = course['código'] ?? course['codigo'] ?? course['code']; + final name = course['nome'] ?? course['name']; + if (code != null && name != null) { + result[code.toString()] = name.toString(); + } + } + if (result.isEmpty) { + throw Exception('Lista de cursos vazia'); + } + return result; + } + + if (data is Map) { + if (data.containsKey('message')) { + throw Exception(data['message'].toString()); + } + return Map.from(data); + } + + throw Exception('Formato de cursos não reconhecido'); +} + class GraduationService { final Dio dio = Dio(); @@ -8,8 +37,7 @@ class GraduationService { try { final response = await dio.get(dotenv.env['API_COURSES']!); if (response.statusCode == 200) { - Map data = response.data; - return data; + return parseGraduationsPayload(response.data); } else { throw Exception('Erro na solicitação GET'); } diff --git a/lib/app/service/user_service.dart b/lib/app/service/user_service.dart index a93e412..7271398 100644 --- a/lib/app/service/user_service.dart +++ b/lib/app/service/user_service.dart @@ -85,4 +85,12 @@ class UserService { userBox.delete('currentCourses'); userBox.delete('grades'); } + + Future>> replaceCurrentCourses( + List courseList) async { + await Hive.initFlutter(); + final userBox = await Hive.openBox('user'); + userBox.put('currentCourses', courseList); + return {'currentCourses': courseList}; + } } diff --git a/lib/app/view/edit_page_view.dart b/lib/app/view/edit_page_view.dart index 77d783d..1cb9c38 100644 --- a/lib/app/view/edit_page_view.dart +++ b/lib/app/view/edit_page_view.dart @@ -404,71 +404,119 @@ class _EditPageState extends State { ? () {} : () { FocusScope.of(context).unfocus(); + editController.setFinalScoreCalcError(false); showDialog( context: context, - builder: (context) { + builder: (dialogContext) { return Observer( builder: (_) => AlertDialog( content: SizedBox( height: 250, - child: Column( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - mainAxisAlignment: - MainAxisAlignment - .spaceAround, - children: [ - const Text( - "Deseja calcular suas notas?", - style: TextStyle( - fontWeight: - FontWeight - .bold, - fontSize: 16), + child: editController + .targetCalcInProgress + ? const Center( + child: SizedBox( + height: 50, + width: 50, + child: + CircularProgressIndicator( + color: AppColors.red, ), - RichText( - text: - const TextSpan( + ), + ) + : editController + .finalScoreCalcError + ? const Center( + child: Text( + "Erro ao calcular a média :(", style: TextStyle( - color: - AppColors - .black, - fontFamily: - 'Poppins', - fontSize: 16), - children: [ - TextSpan( - text: - "É importante dizer que as notas ", - ), - TextSpan( - text: - "calculadas por meta ", - style: TextStyle( - color: AppColors - .red, - fontWeight: - FontWeight - .bold), - ), - TextSpan( - text: - "serão contadas como 0 para a média final.", - ), - ], + color: + AppColors.red, + fontSize: 16, + ), ), + ) + : Column( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisAlignment: + MainAxisAlignment + .spaceAround, + children: [ + const Text( + "Deseja calcular suas notas?", + style: TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 16), + ), + RichText( + text: + const TextSpan( + style: TextStyle( + color: + AppColors.black, + fontFamily: + 'Poppins', + fontSize: 16), + children: [ + TextSpan( + text: + "É importante dizer que as notas ", + ), + TextSpan( + text: + "calculadas por meta ", + style: TextStyle( + color: AppColors.red, + fontWeight: FontWeight.bold), + ), + TextSpan( + text: + "serão contadas como 0 para a média final.", + ), + ], + ), + ), + ]), + ], ), - ]), - ], - ), ), actions: [ editController - .targetCalcInProgress - ? Container() + .targetCalcInProgress || + editController + .finalScoreCalcError + ? editController + .finalScoreCalcError + ? TextButton( + style: TextButton + .styleFrom( + backgroundColor: + AppColors.red, + foregroundColor: + AppColors.white, + shape: RoundedRectangleBorder( + borderRadius: + Round.primary), + minimumSize: + const Size + .fromHeight( + 50), + ), + onPressed: () { + Navigator.pop( + dialogContext); + editController + .setFinalScoreCalcError( + false); + }, + child: const Text( + "Fechar")) + : Container() : TextButton( style: TextButton.styleFrom( backgroundColor: @@ -490,40 +538,48 @@ class _EditPageState extends State { horizontal: 7)), onPressed: () async { + editController + .setFinalScoreCalcError( + false); editController .setTargetCalcProgress( true); - Map - weights = {}; - for (var grade in widget - .course - .exams! + - widget.course - .assignments!) { - weights[grade - .name] = + final weights = + {}; + for (var grade + in widget.course + .exams! + + widget.course + .assignments!) { + weights[grade.name] = grade.weight; } - Navigator.pop( - context); try { - await editController - .calcFinalScore( - weights, - editController - .grades); - final gradesToSave = + final success = + await editController + .calcFinalScore( + weights, + editController + .grades, + ); + if (success) { + final gradesToSave = + editController + .formatGradesForSaving(); + await gradeController + .insertGrades( editController - .formatGradesForSaving(); - gradeController - .insertGrades( - editController - .getCourseCode(), - gradesToSave); - editController - .setTargetCalcProgress( - false); - } catch (e) { + .getCourseCode(), + gradesToSave, + ); + if (dialogContext + .mounted) { + Navigator.pop( + dialogContext); + } + } + } finally { editController .setTargetCalcProgress( false); @@ -577,9 +633,10 @@ class _EditPageState extends State { ? () {} : () { FocusScope.of(context).unfocus(); + editController.setTargetCalcError(false); showDialog( context: context, - builder: (context) { + builder: (dialogContext) { final targetController = TextEditingController(); final ValueNotifier @@ -592,97 +649,124 @@ class _EditPageState extends State { }); return Observer( builder: (_) => AlertDialog( - content: SizedBox( - height: 275, - child: editController - .targetCalcInProgress - ? const Center( - child: SizedBox( - height: 50, - width: 50, - child: - CircularProgressIndicator( - color: AppColors - .red, + content: SingleChildScrollView( + child: SizedBox( + width: double.maxFinite, + child: editController + .targetCalcInProgress + ? const Padding( + padding: EdgeInsets + .symmetric( + vertical: + 24), + child: Center( + child: SizedBox( + height: 50, + width: 50, + child: + CircularProgressIndicator( + color: AppColors + .red, + ), + ), ), - ), - ) - : editController - .targetCalcError - ? const Center( - child: Text( - "Erro ao calcular as notas :(", - style: TextStyle( - color: - AppColors - .red, - fontSize: - 16), - )) - : Column( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - mainAxisAlignment: - MainAxisAlignment - .spaceAround, - children: [ - const Text( - "Como funciona?", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16), - ), - const Text( - "1. Digite a nota que você deseja alcançar na matéria.", - style: TextStyle( - color: AppColors.black, - fontFamily: 'Poppins', - fontSize: 16), - ), - const Text( - "2. Calcularemos as notas necessárias para alcançar essa meta.", - style: TextStyle( - color: AppColors.black, - fontFamily: 'Poppins', - fontSize: 16), - ), - RichText( - text: - const TextSpan( + ) + : editController + .targetCalcError + ? const Padding( + padding: EdgeInsets + .symmetric( + vertical: + 16), + child: Text( + "Erro ao calcular as notas :(", + style: + TextStyle( + color: AppColors + .red, + fontSize: + 16, + ), + ), + ) + : Column( + mainAxisSize: + MainAxisSize + .min, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const Text( + "Como funciona?", + style: TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 16), + ), + const SizedBox( + height: 8), + const Text( + "1. Digite a nota que você deseja alcançar na matéria.", + style: TextStyle( + color: AppColors + .black, + fontFamily: + 'Poppins', + fontSize: 16), + ), + const SizedBox( + height: 4), + const Text( + "2. Calcularemos as notas necessárias para alcançar essa meta.", + style: TextStyle( + color: AppColors + .black, + fontFamily: + 'Poppins', + fontSize: 16), + ), + const SizedBox( + height: 4), + RichText( + text: + const TextSpan( + style: TextStyle( + color: AppColors + .black, + fontFamily: + 'Poppins', + fontSize: 16), + children: [ + TextSpan( + text: + "3. As notas calculadas serão exibidas em ", + ), + TextSpan( + text: + "vermelho.", style: TextStyle( - color: AppColors.black, - fontFamily: 'Poppins', - fontSize: 16), - children: [ - TextSpan( - text: "3. As notas calculadas serão exibidas em ", - ), - TextSpan( - text: "vermelho.", - style: TextStyle(color: AppColors.red, fontWeight: FontWeight.bold), - ), - ], + color: AppColors.red, + fontWeight: FontWeight.bold), ), - ), - ]), - Expanded( - child: - Container()), - GradeInput( - changes: - false, - name: - "Sua meta", - controller: - targetController, - labelled: - true, - ), - ], - ), + ], + ), + ), + const SizedBox( + height: 16), + GradeInput( + changes: + false, + name: + "Sua meta", + controller: + targetController, + labelled: + true, + ), + ], + ), + ), ), actions: [ editController @@ -710,7 +794,7 @@ class _EditPageState extends State { 7)), onPressed: () { Navigator.pop( - context); + dialogContext); editController .setTargetCalcError( false); @@ -728,7 +812,7 @@ class _EditPageState extends State { bool>( valueListenable: isButtonDisabled, - builder: (context, + builder: (_, isDisabled, child) => Observer( @@ -753,20 +837,24 @@ class _EditPageState extends State { onPressed: isDisabled ? null : () async { - Navigator.pop(context); + editController.setTargetCalcError(false); editController.setTargetCalcProgress(true); editController.setTargetGrade(double.parse(targetController.text)); - Map weights = {}; + final weights = {}; for (var grade in widget.course.exams! + widget.course.assignments!) { weights[grade.name] = grade.weight; } try { - await editController.calcTargetGrade(editController.grades, weights); - final gradesToSave = editController.formatGradesForSaving(); - gradeController.insertGrades(editController.getCourseCode(), gradesToSave); - editController.setTargetCalcProgress(false); - } catch (e) { + final success = await editController.calcTargetGrade(editController.grades, weights); + if (success) { + final gradesToSave = editController.formatGradesForSaving(); + await gradeController.insertGrades(editController.getCourseCode(), gradesToSave); + if (dialogContext.mounted) { + Navigator.pop(dialogContext); + } + } + } finally { editController.setTargetCalcProgress(false); } }, diff --git a/lib/app/view/home_page_view.dart b/lib/app/view/home_page_view.dart index b422b33..9de7235 100644 --- a/lib/app/view/home_page_view.dart +++ b/lib/app/view/home_page_view.dart @@ -44,60 +44,51 @@ class _HomePageState extends State { return AnnotatedRegion( value: const SystemUiOverlayStyle( statusBarColor: Colors.transparent, - statusBarIconBrightness: Brightness.light, // Ícones brancos - statusBarBrightness: Brightness.dark, // Para iOS - systemNavigationBarColor: Colors.white, - systemNavigationBarIconBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.light, + statusBarBrightness: Brightness.dark, + systemNavigationBarColor: Colors.transparent, + systemNavigationBarDividerColor: Colors.transparent, + systemNavigationBarIconBrightness: Brightness.light, + systemNavigationBarContrastEnforced: false, ), child: Scaffold( resizeToAvoidBottomInset: false, backgroundColor: AppColors.background, drawer: const AppDrawer(), - body: Padding( - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - child: Center( - child: FractionallySizedBox( - widthFactor: 1, - child: Padding( - padding: const EdgeInsets.only(top: 56.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - //Top Barra de Navegação sem botão de voltar - NavigationTopBar( - prevPage: commonController.getPreviousPage, - isHomePage: true, - ), - // Botão Adicionar Matérias - const Padding( - padding: EdgeInsets.symmetric(vertical: 16), - child: AddCourseNavigationButton(), - ), - // Lista de Matérias - Observer( - builder: (_) => coursesController.loadedCourses - ? userController.currentCourses.isNotEmpty - ? SizedBox( - height: MediaQuery.of(context).size.height * - 0.7 - - 13, - child: Column( - children: [ - Expanded( - child: ListView.builder( - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: userController + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NavigationTopBar( + prevPage: commonController.getPreviousPage, + isHomePage: true, + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: AddCourseNavigationButton(), + ), + Expanded( + child: Observer( + builder: (_) => coursesController.loadedCourses + ? userController.currentCourses.isNotEmpty + ? ListView.builder( + padding: EdgeInsets.zero, + itemCount: userController .currentCourses.length, itemBuilder: (context, index) { - CourseModel? course = - coursesController.allCourses?[ - userController - .currentCourses[ - index]]; + final courseCode = userController + .currentCourses[index]; + final CourseModel? course = + coursesController + .allCourses?[courseCode]; + if (course == null) { + return const SizedBox.shrink(); + } return FutureBuilder( future: updateFinalScore( - course!.code), + course.code), builder: (context, snapshot) { if (snapshot .connectionState == @@ -250,57 +241,29 @@ class _HomePageState extends State { }, ); }, - ), - ), - ], - ), - ) - : SizedBox( - height: MediaQuery.of(context).size.height * - 0.7, - child: const Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Center( - child: Text( - "Você não tem matérias cadastradas", - style: TextStyle( - fontSize: 16.0, - fontWeight: - FontWeight.w600, - color: AppColors.white), - textAlign: TextAlign.center, - ), - ), - ], - ), - ), - ], - ), - ) - : SizedBox( - height: - MediaQuery.of(context).size.height * 0.7, - width: double.maxFinite, - child: const Center( - child: SizedBox( - width: 50, - height: 50, - child: CircularProgressIndicator( - color: AppColors.red, + ) + : const Center( + child: Text( + "Você não tem matérias cadastradas", + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.w600, + color: AppColors.white, ), + textAlign: TextAlign.center, ), + ) + : const Center( + child: SizedBox( + width: 50, + height: 50, + child: CircularProgressIndicator( + color: AppColors.red, ), - )), - ], + ), + )), ), - ), + ], ), ), ), @@ -320,13 +283,16 @@ class _HomePageState extends State { } void _scheduleNotificationsDialog() { + if (noticeDismissedThisAppSession) return; + WidgetsBinding.instance.addPostFrameCallback((_) async { - if (!mounted) return; + if (!mounted || noticeDismissedThisAppSession) return; await _loadAndShowNotificationsIfAny(context); }); } Future _loadAndShowNotificationsIfAny(BuildContext pageContext) async { + if (noticeDismissedThisAppSession) return; final notificationsUrl = dotenv.env['API_NOTIFICATIONS']; final notificationsConfigured = notificationsUrl != null && notificationsUrl.trim().isNotEmpty; diff --git a/lib/app/view/loading_page_view.dart b/lib/app/view/loading_page_view.dart index d02e6f3..100fadd 100644 --- a/lib/app/view/loading_page_view.dart +++ b/lib/app/view/loading_page_view.dart @@ -26,7 +26,8 @@ class _LoadingPageState extends State { loadingPageController.setLoading(true); final userDataMissing = await userController.checkUserDataExists(); try { - coursesController.setAllCourses(await coursesController.fetchCourses()); + final refreshed = await coursesController.refreshAllSubjects(); + if (!refreshed) throw Exception('Falha ao atualizar disciplinas'); } catch (e) { Navigator.pushReplacementNamed(context, '/disconnected'); } diff --git a/lib/app/widgets/common/app_lifecycle_refresh.dart b/lib/app/widgets/common/app_lifecycle_refresh.dart new file mode 100644 index 0000000..abc8d32 --- /dev/null +++ b/lib/app/widgets/common/app_lifecycle_refresh.dart @@ -0,0 +1,38 @@ +import 'package:dev_medias_front_flutter/app/controller/common/courses_controller.dart'; +import 'package:dev_medias_front_flutter/app/controller/intro_page_controller.dart'; +import 'package:flutter/material.dart'; + +/// Refaz o GET de disciplinas quando o app volta do background. +class AppLifecycleRefresh extends StatefulWidget { + const AppLifecycleRefresh({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _AppLifecycleRefreshState(); +} + +class _AppLifecycleRefreshState extends State + with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state != AppLifecycleState.resumed) return; + if (!introPageController.loggedIn) return; + coursesController.refreshAllSubjects(); + } + + @override + Widget build(BuildContext context) => widget.child; +} diff --git a/lib/app/widgets/notice_card.dart b/lib/app/widgets/notice_card.dart index 1b44062..6c02c52 100644 --- a/lib/app/widgets/notice_card.dart +++ b/lib/app/widgets/notice_card.dart @@ -4,26 +4,26 @@ import 'package:flutter/material.dart'; const String _kDefaultNoticeTitle = 'Aviso'; const String _kDefaultNoticeBody = ''' -Este é um aviso estático de exemplo. Quando a integração com a API estiver pronta, o título e o texto virão do servidor. - -A Dev Community Mauá pode publicar avisos sobre manutenção, mudanças no cálculo de médias ou novidades do aplicativo. Leia com atenção antes de continuar. Lembre-se de conferir suas notas oficiais no sistema da instituição. Este aplicativo é uma ferramenta de apoio e pode conter erros ou interpretações diferentes do regulamento vigente. -Ao usar o app, você concorda em manter seus dados de cadastro atualizados e em não compartilhar credenciais de acesso com terceiros. - Se encontrar inconsistências, utilize o canal de suporte para reportar o problema com o máximo de detalhes possível. Obrigado por utilizar o DevMédias. Role até o final deste texto para habilitar o botão de confirmação. '''; +/// Aviso já fechado nesta execução do app; reinicia ao fechar e reabrir o app. +bool noticeDismissedThisAppSession = false; + /// Mostra o card de aviso em tela quase cheia; só fecha após rolar até o fim e tocar em ENTENDI. Future showBlockingNoticeDialog( BuildContext context, { String? title, String? description, -}) { - return showDialog( +}) async { + if (noticeDismissedThisAppSession) return; + + await showDialog( context: context, barrierDismissible: false, builder: (dialogContext) { @@ -100,6 +100,7 @@ class _NoticeCardState extends State { } void _onEntendi() { + noticeDismissedThisAppSession = true; Navigator.of(context).pop(); } diff --git a/lib/main.dart b/lib/main.dart index 59cdf90..409885a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:dev_medias_front_flutter/app/widgets/common/app_lifecycle_refresh.dart'; import 'package:dev_medias_front_flutter/app/view/add_page_view.dart'; import 'package:dev_medias_front_flutter/app/view/edit_page_view.dart'; import 'package:dev_medias_front_flutter/app/view/home_page_view.dart'; @@ -29,7 +30,8 @@ class MyApp extends StatelessWidget { designSize: const Size(375, 812), minTextAdapt: true, splitScreenMode: true, - builder: (context, child) => MaterialApp( + builder: (context, child) => AppLifecycleRefresh( + child: MaterialApp( title: 'DevMédias', theme: ThemeData( useMaterial3: true, @@ -57,6 +59,7 @@ class MyApp extends StatelessWidget { }, '/disconnected': (_) => const NoConnectionPage(), }, + ), ), ); } diff --git a/test/grade_api_payload_test.dart b/test/grade_api_payload_test.dart new file mode 100644 index 0000000..3b27712 --- /dev/null +++ b/test/grade_api_payload_test.dart @@ -0,0 +1,23 @@ +import 'package:dev_medias_front_flutter/app/model/course.dart'; +import 'package:dev_medias_front_flutter/app/model/grade.dart'; +import 'package:dev_medias_front_flutter/app/service/grade_api_payload.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('normalizeCourseComponentWeight aceita fração e percentual', () { + expect(normalizeCourseComponentWeight(0.6), 0.6); + expect(normalizeCourseComponentWeight(60), 0.6); + }); + + test('isExamGrade usa nomes da matéria, não a primeira letra', () { + final course = CourseModel( + code: 'CIC401', + exams: [GradeModel(name: 'Primeira Prova Bimestral', weight: 0.4)], + assignments: [GradeModel(name: 'Trabalho 1', weight: 0.5)], + ); + + expect(isExamGrade('Primeira Prova Bimestral', course), isTrue); + expect(isExamGrade('Trabalho 1', course), isFalse); + expect(isExamGrade('Prova P1', course), isFalse); + }); +} diff --git a/test/graduation_service_test.dart b/test/graduation_service_test.dart new file mode 100644 index 0000000..42231f4 --- /dev/null +++ b/test/graduation_service_test.dart @@ -0,0 +1,34 @@ +import 'package:dev_medias_front_flutter/app/service/graduation_service.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('parseGraduationsPayload', () { + test('lista com código/nome vira mapa por sigla', () { + final parsed = parseGraduationsPayload([ + {'código': 'CIC', 'nome': 'Ciência da Computação'}, + {'código': 'EAL', 'nome': 'Engenharia de Alimentos'}, + ]); + + expect(parsed['CIC'], 'Ciência da Computação'); + expect(parsed['EAL'], 'Engenharia de Alimentos'); + }); + + test('aceita codigo sem acento', () { + final parsed = parseGraduationsPayload([ + {'codigo': 'ADM', 'name': 'Administração'}, + ]); + + expect(parsed['ADM'], 'Administração'); + }); + + test('mapa antigo do CDN continua válido', () { + final parsed = parseGraduationsPayload({ + 'CIC': 'Ciência da Computação', + 'ADM': 'Administração', + }); + + expect(parsed['CIC'], 'Ciência da Computação'); + expect(parsed.length, 2); + }); + }); +} From db1c67449a8c51f5fae695d0b3411904dac07027 Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 2 Jul 2026 12:43:37 -0300 Subject: [PATCH 17/18] fix: contact us popup --- .../controller/common/support_controller.dart | 16 ++-- .../common/support_controller.g.dart | 2 +- lib/app/service/support_service.dart | 19 +++- lib/app/widgets/popup_report_error.dart | 93 +++++++++++++++---- lib/app/widgets/popup_sent_error.dart | 3 +- 5 files changed, 100 insertions(+), 33 deletions(-) diff --git a/lib/app/controller/common/support_controller.dart b/lib/app/controller/common/support_controller.dart index 7fbad83..6b9cbd2 100644 --- a/lib/app/controller/common/support_controller.dart +++ b/lib/app/controller/common/support_controller.dart @@ -23,17 +23,17 @@ abstract class _SupportControllerBase with Store { TextEditingController get getMessageController => messageController; @action - Future sendSupport() async { + Future> sendSupport() async { final message = { - "email": emailController.text, - "message": messageController.text, + "email": emailController.text.trim(), + "message": messageController.text.trim(), }; - try { - final response = await supportService.postMessage(message); - return response; - } catch (e) { - throw Exception('Erro de rede: $e'); + final response = await supportService.postMessage(message); + if (response['success'] == true) { + emailController.clear(); + messageController.clear(); } + return response; } } diff --git a/lib/app/controller/common/support_controller.g.dart b/lib/app/controller/common/support_controller.g.dart index c09e21e..b70d3e0 100644 --- a/lib/app/controller/common/support_controller.g.dart +++ b/lib/app/controller/common/support_controller.g.dart @@ -62,7 +62,7 @@ mixin _$SupportController on _SupportControllerBase, Store { AsyncAction('_SupportControllerBase.sendSupport', context: context); @override - Future> sendSupport() { + Future> sendSupport() { return _$sendSupportAsyncAction.run(() => super.sendSupport()); } diff --git a/lib/app/service/support_service.dart b/lib/app/service/support_service.dart index f806ce8..2bb4d2b 100644 --- a/lib/app/service/support_service.dart +++ b/lib/app/service/support_service.dart @@ -1,20 +1,29 @@ +import 'package:dev_medias_front_flutter/app/service/grade_api_payload.dart'; import 'package:dio/dio.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; class SupportService { final Dio dio = Dio(); - Future postMessage(Map message) async { + Future> postMessage(Map message) async { try { final response = await dio.post(dotenv.env['API_SUPPORT_URL']!, data: message); if (response.statusCode == 200) { - return message; - } else { - throw Exception('Erro na solicitação POST'); + return {'success': true}; } + return { + 'success': false, + 'error': errorMessageFromApiBody(response.data) ?? + 'Erro na solicitação POST (${response.statusCode})', + }; + } on DioException catch (e) { + final error = errorMessageFromApiBody(e.response?.data) ?? + e.message ?? + 'Erro de rede'; + return {'success': false, 'error': error}; } catch (e) { - throw Exception('Erro de rede: $e'); + return {'success': false, 'error': 'Erro de rede: $e'}; } } } diff --git a/lib/app/widgets/popup_report_error.dart b/lib/app/widgets/popup_report_error.dart index 9baaae8..f390625 100644 --- a/lib/app/widgets/popup_report_error.dart +++ b/lib/app/widgets/popup_report_error.dart @@ -13,6 +13,64 @@ class PopupReportError extends StatefulWidget { class _PopupReportErrorState extends State { final GlobalKey _formKey = GlobalKey(); + bool _isSending = false; + + Future _submitSupport() async { + final email = supportController.emailController.text.trim(); + final message = supportController.messageController.text.trim(); + + if (email.isEmpty || message.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Preencha o e-mail e a descrição antes de enviar.'), + backgroundColor: AppColors.red, + ), + ); + return; + } + + setState(() => _isSending = true); + + try { + final result = await supportController.sendSupport(); + + if (!mounted) return; + + if (result['success'] == true) { + Navigator.of(context).pop(); + await showDialog( + context: context, + builder: (BuildContext context) { + return const PopupErrorSent(); + }, + ); + return; + } + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + result['error']?.toString() ?? 'Não foi possível enviar a mensagem.', + ), + backgroundColor: AppColors.red, + ), + ); + } catch (e) { + if (!mounted) return; + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Erro inesperado ao enviar: $e'), + backgroundColor: AppColors.red, + ), + ); + } finally { + if (mounted) { + setState(() => _isSending = false); + } + } + } + @override Widget build(BuildContext context) { return Observer( @@ -143,23 +201,24 @@ class _PopupReportErrorState extends State { ), fixedSize: const Size(150, 50), ), - onPressed: () { - supportController.sendSupport(); - showDialog( - context: context, - builder: (BuildContext context) { - return const PopupErrorSent(); - }, - ); - }, - child: const Text( - "Enviar", - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w600, - color: AppColors.white, - ), - ), + onPressed: _isSending ? null : _submitSupport, + child: _isSending + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: AppColors.white, + ), + ) + : const Text( + "Enviar", + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.w600, + color: AppColors.white, + ), + ), ), ], ), diff --git a/lib/app/widgets/popup_sent_error.dart b/lib/app/widgets/popup_sent_error.dart index 18d04cc..2bd507c 100644 --- a/lib/app/widgets/popup_sent_error.dart +++ b/lib/app/widgets/popup_sent_error.dart @@ -65,8 +65,7 @@ class PopupErrorSent extends StatelessWidget { fixedSize: const Size(150, 50), ), onPressed: () { - // Fecha ambos os popups e retorna à página anterior - Navigator.of(context).pop; + Navigator.of(context).pop(); }, child: const Text( "Concluir", From 6a0573f27e270b205d7934fccb66268ef8df31ad Mon Sep 17 00:00:00 2001 From: Leo Iorio Date: Thu, 2 Jul 2026 12:43:59 -0300 Subject: [PATCH 18/18] chore: bump iOS deployment target to 13.0 --- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile | 2 +- ios/Podfile.lock | 8 ++++---- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- .../xcshareddata/xcschemes/Runner.xcscheme | 3 +++ 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 310f180..3d62243 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/ios/Podfile b/ios/Podfile index d97f17e..e51a31d 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +# platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2d4708e..5702378 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -21,10 +21,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" SPEC CHECKSUMS: - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb -PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 +PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694 COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 3703d84..08dcd71 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -454,7 +454,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -585,7 +585,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -636,7 +636,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8e3ca5d..e3773d4 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES">