diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 16a7c37c3d..94fb1be737 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -60,10 +60,11 @@ jobs:
- name: Get Packages
run: flutter pub get
- - name: Configure Dev env
- run: echo DEV_HOST=$DEV_HOST >> .env
+ - name: Configure Dev config.json
+ run: |
+ printf "%s" "$CONFIG" > config.json
env:
- DEV_HOST: ${{ vars.DEV_HOST }}
+ CONFIG: ${{ vars.DEV_CONFIG }}
- name: Add Firebase configuration for Dev
run: |
@@ -72,7 +73,7 @@ jobs:
GOOGLE_SERVICES_DEV_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_DEV_JSON_BASE64 }}
- name: Build Dev 🔧
- run: flutter build ${{ matrix.target }}
+ run: flutter build ${{ matrix.target }} --dart-define-from-file=config.json
- name: Upload Artifacts
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/release-mobile.yml b/.github/workflows/release-mobile.yml
index a490d11bb1..7623f778aa 100644
--- a/.github/workflows/release-mobile.yml
+++ b/.github/workflows/release-mobile.yml
@@ -71,27 +71,19 @@ jobs:
- name: Get Packages
run: flutter pub get
- - name: Configure Alpha env
+ - name: Configure Alpha config.json
if: needs.extract-version.outputs.isAlpha == 'true'
run: |
- echo ALPHA_HOST=$ALPHA_HOST >> .env
- echo PLAUSIBLE_HOST=$PLAUSIBLE_HOST >> .env
- echo PLAUSIBLE_DOMAIN=$PLAUSIBLE_DOMAIN >> .env
+ printf "%s" "$CONFIG" > config.json
env:
- ALPHA_HOST: ${{ vars.ALPHA_HOST }}
- PLAUSIBLE_HOST: ${{ secrets.PLAUSIBLE_ALPHA_HOST }}
- PLAUSIBLE_DOMAIN: ${{ secrets.PLAUSIBLE_ALPHA_DOMAIN }}
-
- - name: Configure production env
+ CONFIG: ${{ vars.ALPHA_CONFIG }}
+
+ - name: Configure Alpha config.json
if: needs.extract-version.outputs.isAlpha == 'false'
run: |
- echo PROD_HOST=$PROD_HOST >> .env
- echo PLAUSIBLE_HOST=$PLAUSIBLE_HOST >> .env
- echo PLAUSIBLE_DOMAIN=$PLAUSIBLE_DOMAIN >> .env
+ printf "%s" "$CONFIG" > config.json
env:
- PROD_HOST: ${{ vars.PROD_HOST }}
- PLAUSIBLE_HOST: ${{ secrets.PLAUSIBLE_HOST }}
- PLAUSIBLE_DOMAIN: ${{ secrets.PLAUSIBLE_DOMAIN }}
+ CONFIG: ${{ vars.PROD_CONFIG }}
- name: Add Firebase configuration for Alpha
if: needs.extract-version.outputs.isAlpha == 'true'
@@ -127,11 +119,11 @@ jobs:
- name: Build Alpha 🔧
if: needs.extract-version.outputs.isAlpha == 'true'
- run: flutter build ${{ matrix.target }} --flavor=alpha --release --build-name ${{ needs.extract-version.outputs.versionName }} --build-number ${{ needs.extract-version.outputs.versionCode }}
+ run: flutter build ${{ matrix.target }} --flavor=alpha --dart-define-from-file=config.json --release --build-name ${{ needs.extract-version.outputs.versionName }} --build-number ${{ needs.extract-version.outputs.versionCode }}
- name: Build production 🔧
if: needs.extract-version.outputs.isAlpha == 'false'
- run: flutter build ${{ matrix.target }} --flavor=prod --release --build-name ${{ needs.extract-version.outputs.versionName }} --build-number ${{ needs.extract-version.outputs.versionCode }}
+ run: flutter build ${{ matrix.target }} --flavor=prod --dart-define-from-file=config.json --release --build-name ${{ needs.extract-version.outputs.versionName }} --build-number ${{ needs.extract-version.outputs.versionCode }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml
index 9259e18547..d3d3fdd774 100644
--- a/.github/workflows/release-web.yml
+++ b/.github/workflows/release-web.yml
@@ -54,31 +54,65 @@ jobs:
- name: Get Packages
run: flutter pub get
+ - name: Configure web files
+ run: |
+ sed -i 's|{{ APP_NAME }}|'"$APP_NAME"'|g' web/index.html
+ sed -i 's|{{ SCHOOL_NAME }}|'"$SCHOOL_NAME"'|g' web/index.html
+ sed -i 's|{{ ENTITY_NAME }}|'"$ENTITY_NAME"'|g' web/index.html
+ env:
+ APP_NAME: ${{ vars.APP_NAME }}
+ SCHOOL_NAME: ${{ vars.SCHOOL_NAME }}
+ ENTITY_NAME: ${{ vars.ENTITY_NAME }}
+
+ - name: Move assets
+ run: |
+ cp -r -f $LOGIN_ASSET_PATH assets/images/login.webp
+ cp -r -f $BACK_ASSET_PATH assets/web/back.webp
+ cp -r -f $LOGO_ASSET_PATH assets/images/logo_prod.png
+ cp -r -f $ICON_ASSET_PATH assets/images/icon_prod.png
+ cp -r -f $ICON_ANDROID_PATH/* android/app/src/prod/
+ cp -r -f $ICON_IOS_PATH/* ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/
+ cp -r -f $ICON_WEB_PATH/* web/Prod/
+ env:
+ LOGIN_ASSET_PATH: ${{ vars.LOGIN_ASSET_PATH }}
+ BACK_ASSET_PATH: ${{ vars.BACK_ASSET_PATH }}
+ LOGO_ASSET_PATH: ${{ vars.LOGO_ASSET_PATH }}
+ ICON_ASSET_PATH: ${{ vars.ICON_ASSET_PATH }}
+ ICON_ANDROID_PATH: ${{ vars.ICON_ANDROID_PATH }}
+ ICON_IOS_PATH: ${{ vars.ICON_IOS_PATH }}
+ ICON_WEB_PATH: ${{ vars.ICON_WEB_PATH }}
+
- name: Configure env
run: |
echo PROD_HOST=$PROD_HOST >> .env
echo ALPHA_HOST=$ALPHA_HOST >> .env
+ echo PAYMENT_NAME=$PAYMENT_NAME >> .env
+ echo SCHOOL_NAME=$SCHOOL_NAME >> .env
+ echo APP_NAME=$APP_NAME >> .env
+ echo APP_ID_PREFIX=$APP_ID_PREFIX >> .env
+ echo TITAN_URL=$TITAN_URL >> .env
env:
PROD_HOST: ${{ vars.PROD_HOST }}
ALPHA_HOST: ${{ vars.ALPHA_HOST }}
+ PAYMENT_NAME: ${{ vars.PAYMENT_NAME }}
+ SCHOOL_NAME: ${{ vars.SCHOOL_NAME }}
+ APP_NAME: ${{ vars.APP_NAME }}
+ APP_ID_PREFIX: ${{ vars.APP_ID_PREFIX }}
+ TITAN_URL: ${{ vars.TITAN_URL }}
- name: Configure Alpha env
if: needs.extract-version.outputs.isAlpha == 'true'
run: |
- echo PLAUSIBLE_HOST=$PLAUSIBLE_ALPHA_HOST >> .env
- echo PLAUSIBLE_DOMAIN=$PLAUSIBLE_ALPHA_DOMAIN >> .env
+ printf "%s" "$CONFIG" > config.json
env:
- PLAUSIBLE_ALPHA_HOST: ${{ secrets.PLAUSIBLE_ALPHA_HOST }}
- PLAUSIBLE_ALPHA_DOMAIN: ${{ secrets.PLAUSIBLE_ALPHA_DOMAIN }}
+ CONFIG: ${{ vars.ALPHA_CONFIG }}
- - name: Configure Prod env
+ - name: Configure Prod config.json
if: needs.extract-version.outputs.isAlpha == 'false'
run: |
- echo PLAUSIBLE_HOST=$PLAUSIBLE_PROD_HOST >> .env
- echo PLAUSIBLE_DOMAIN=$PLAUSIBLE_PROD_DOMAIN >> .env
+ printf "%s" "$CONFIG" > config.json
env:
- PLAUSIBLE_PROD_HOST: ${{ secrets.PLAUSIBLE_HOST }}
- PLAUSIBLE_PROD_DOMAIN: ${{ secrets.PLAUSIBLE_DOMAIN }}
+ CONFIG: ${{ vars.PROD_CONFIG }}
- name: Set Alpha icons
if: needs.extract-version.outputs.isAlpha == 'true'
@@ -90,15 +124,20 @@ jobs:
if: needs.extract-version.outputs.isAlpha == 'false'
run: |
cp -f web/Prod/favicon.png web/favicon.png
+ mkdir -p web/icons
cp -f web/Prod/icons/* web/icons/
- - name: Build Alpha 🔧
- if: needs.extract-version.outputs.isAlpha == 'true'
- run: flutter build web --release --dart-define=flavor=alpha
-
- - name: Build Prod 🔧
- if: needs.extract-version.outputs.isAlpha == 'false'
- run: flutter build web --release --dart-define=flavor=prod
+ - name: Replace variables in index.html and manifest.json
+ run: |
+ APP_NAME=$(cat config.json | jq -r '.APP_NAME')
+ SCHOOL_NAME=$(cat config.json | jq -r '.SCHOOL_NAME')
+ sed -i "s/{{APP_NAME}}/$APP_NAME/g" web/index.html
+ sed -i "s/{{SCHOOL_NAME}}/$SCHOOL_NAME/g" web/index.html
+ sed -i "s/{{APP_NAME}}/$APP_NAME/g" web/manifest.json
+ sed -i "s/{{SCHOOL_NAME}}/$SCHOOL_NAME/g" web/manifest.json
+
+ - name: Build 🔧
+ run: flutter build web --release --dart-define-from-file=config.json
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
diff --git a/.gitignore b/.gitignore
index 3a2448718e..beb797db5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,10 +44,13 @@ app.*.map.json
/android/app/profile
/android/app/release
-# env
-.env
+# Config
+config/*
+!config/config-template.json
# Firebase
+google-services.json
+GoogleService-Info.plist
android/app/src/alpha/google-services.json
android/app/src/dev/google-services.json
android/app/src/prod/google-services.json
@@ -63,3 +66,27 @@ coverage/
# Platforms not supported by this project
/windows/
+
+# Missing translations
+missing.txt
+
+# Fastlane
+android/fastlane/metadata
+android/fastlane/report.xml
+ios/fastlane/report.xml
+android/fastlane-service-account.json
+ios/app-store-connect-api.p8
+
+# Generated xcode configuration from dart defines
+Dart-Defines.xcconfig
+
+# Config
+config/
+!config/config-template.json
+
+# Folder to store various keys and secrets
+keys
+
+MainActivity.kt
+
+.env
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index f15439e506..6cab8f2e9e 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -11,8 +11,7 @@
"args": [
"--flavor",
"dev",
- "--dart-define",
- "flavor=dev",
+ "--dart-define-from-file=config/config-dev.json",
"--web-port",
"3000"
]
@@ -25,8 +24,7 @@
"args": [
"--flavor",
"dev",
- "--dart-define",
- "flavor=dev",
+ "--dart-define-from-file=config/config-dev.json",
"--web-port",
"3000"
]
@@ -38,8 +36,7 @@
"args": [
"--flavor",
"alpha",
- "--dart-define",
- "flavor=alpha",
+ "--dart-define-from-file=config/config-alpha.json",
"--web-port",
"3000"
]
@@ -51,8 +48,7 @@
"args": [
"--flavor",
"prod",
- "--dart-define",
- "flavor=prod",
+ "--dart-define-from-file=config/config-prod.json",
"--web-port",
"3000"
]
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000000..a5d4c5a9e1
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,4 @@
+source "https://rubygems.org"
+
+gem "abbrev"
+gem "fastlane"
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000000..f07f6be8b1
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,231 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ CFPropertyList (3.0.7)
+ base64
+ nkf
+ rexml
+ abbrev (0.1.2)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
+ artifactory (3.0.17)
+ atomos (0.1.3)
+ aws-eventstream (1.4.0)
+ aws-partitions (1.1147.0)
+ aws-sdk-core (3.229.0)
+ aws-eventstream (~> 1, >= 1.3.0)
+ aws-partitions (~> 1, >= 1.992.0)
+ aws-sigv4 (~> 1.9)
+ base64
+ bigdecimal
+ jmespath (~> 1, >= 1.6.1)
+ logger
+ aws-sdk-kms (1.110.0)
+ aws-sdk-core (~> 3, >= 3.228.0)
+ aws-sigv4 (~> 1.5)
+ aws-sdk-s3 (1.196.1)
+ aws-sdk-core (~> 3, >= 3.228.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.5)
+ aws-sigv4 (1.12.1)
+ aws-eventstream (~> 1, >= 1.0.2)
+ babosa (1.0.4)
+ base64 (0.3.0)
+ bigdecimal (3.2.2)
+ claide (1.1.0)
+ colored (1.2)
+ colored2 (3.1.2)
+ commander (4.6.0)
+ highline (~> 2.0.0)
+ declarative (0.0.20)
+ digest-crc (0.7.0)
+ rake (>= 12.0.0, < 14.0.0)
+ domain_name (0.6.20240107)
+ dotenv (2.8.1)
+ emoji_regex (3.2.3)
+ excon (0.112.0)
+ faraday (1.10.4)
+ faraday-em_http (~> 1.0)
+ faraday-em_synchrony (~> 1.0)
+ faraday-excon (~> 1.1)
+ faraday-httpclient (~> 1.0)
+ faraday-multipart (~> 1.0)
+ faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.0)
+ faraday-patron (~> 1.0)
+ faraday-rack (~> 1.0)
+ faraday-retry (~> 1.0)
+ ruby2_keywords (>= 0.0.4)
+ faraday-cookie_jar (0.0.7)
+ faraday (>= 0.8.0)
+ http-cookie (~> 1.0.0)
+ faraday-em_http (1.0.0)
+ faraday-em_synchrony (1.0.1)
+ faraday-excon (1.1.0)
+ faraday-httpclient (1.0.1)
+ faraday-multipart (1.1.1)
+ multipart-post (~> 2.0)
+ faraday-net_http (1.0.2)
+ faraday-net_http_persistent (1.2.0)
+ faraday-patron (1.0.0)
+ faraday-rack (1.0.0)
+ faraday-retry (1.0.3)
+ faraday_middleware (1.2.1)
+ faraday (~> 1.0)
+ fastimage (2.4.0)
+ fastlane (2.228.0)
+ CFPropertyList (>= 2.3, < 4.0.0)
+ addressable (>= 2.8, < 3.0.0)
+ artifactory (~> 3.0)
+ aws-sdk-s3 (~> 1.0)
+ babosa (>= 1.0.3, < 2.0.0)
+ bundler (>= 1.12.0, < 3.0.0)
+ colored (~> 1.2)
+ commander (~> 4.6)
+ dotenv (>= 2.1.1, < 3.0.0)
+ emoji_regex (>= 0.1, < 4.0)
+ excon (>= 0.71.0, < 1.0.0)
+ faraday (~> 1.0)
+ faraday-cookie_jar (~> 0.0.6)
+ faraday_middleware (~> 1.0)
+ fastimage (>= 2.1.0, < 3.0.0)
+ fastlane-sirp (>= 1.0.0)
+ gh_inspector (>= 1.1.2, < 2.0.0)
+ google-apis-androidpublisher_v3 (~> 0.3)
+ google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-env (>= 1.6.0, < 2.0.0)
+ google-cloud-storage (~> 1.31)
+ highline (~> 2.0)
+ http-cookie (~> 1.0.5)
+ json (< 3.0.0)
+ jwt (>= 2.1.0, < 3)
+ mini_magick (>= 4.9.4, < 5.0.0)
+ multipart-post (>= 2.0.0, < 3.0.0)
+ naturally (~> 2.2)
+ optparse (>= 0.1.1, < 1.0.0)
+ plist (>= 3.1.0, < 4.0.0)
+ rubyzip (>= 2.0.0, < 3.0.0)
+ security (= 0.1.5)
+ simctl (~> 1.6.3)
+ terminal-notifier (>= 2.0.0, < 3.0.0)
+ terminal-table (~> 3)
+ tty-screen (>= 0.6.3, < 1.0.0)
+ tty-spinner (>= 0.8.0, < 1.0.0)
+ word_wrap (~> 1.0.0)
+ xcodeproj (>= 1.13.0, < 2.0.0)
+ xcpretty (~> 0.4.1)
+ xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
+ fastlane-sirp (1.0.0)
+ sysrandom (~> 1.0)
+ gh_inspector (1.1.3)
+ google-apis-androidpublisher_v3 (0.54.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-core (0.11.3)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (>= 0.16.2, < 2.a)
+ httpclient (>= 2.8.1, < 3.a)
+ mini_mime (~> 1.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.a)
+ rexml
+ google-apis-iamcredentials_v1 (0.17.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.13.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-storage_v1 (0.31.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-cloud-core (1.8.0)
+ google-cloud-env (>= 1.0, < 3.a)
+ google-cloud-errors (~> 1.0)
+ google-cloud-env (1.6.0)
+ faraday (>= 0.17.3, < 3.0)
+ google-cloud-errors (1.5.0)
+ google-cloud-storage (1.47.0)
+ addressable (~> 2.8)
+ digest-crc (~> 0.4)
+ google-apis-iamcredentials_v1 (~> 0.1)
+ google-apis-storage_v1 (~> 0.31.0)
+ google-cloud-core (~> 1.6)
+ googleauth (>= 0.16.2, < 2.a)
+ mini_mime (~> 1.0)
+ googleauth (1.8.1)
+ faraday (>= 0.17.3, < 3.a)
+ jwt (>= 1.4, < 3.0)
+ multi_json (~> 1.11)
+ os (>= 0.9, < 2.0)
+ signet (>= 0.16, < 2.a)
+ highline (2.0.3)
+ http-cookie (1.0.8)
+ domain_name (~> 0.5)
+ httpclient (2.9.0)
+ mutex_m
+ jmespath (1.6.2)
+ json (2.13.2)
+ jwt (2.10.2)
+ base64
+ logger (1.7.0)
+ mini_magick (4.13.2)
+ mini_mime (1.1.5)
+ multi_json (1.17.0)
+ multipart-post (2.4.1)
+ mutex_m (0.3.0)
+ nanaimo (0.4.0)
+ naturally (2.3.0)
+ nkf (0.2.0)
+ optparse (0.6.0)
+ os (1.1.4)
+ plist (3.7.2)
+ public_suffix (6.0.2)
+ rake (13.3.0)
+ representable (3.2.0)
+ declarative (< 0.1.0)
+ trailblazer-option (>= 0.1.1, < 0.2.0)
+ uber (< 0.2.0)
+ retriable (3.1.2)
+ rexml (3.4.1)
+ rouge (3.28.0)
+ ruby2_keywords (0.0.5)
+ rubyzip (2.4.1)
+ security (0.1.5)
+ signet (0.20.0)
+ addressable (~> 2.8)
+ faraday (>= 0.17.5, < 3.a)
+ jwt (>= 1.5, < 3.0)
+ multi_json (~> 1.10)
+ simctl (1.6.10)
+ CFPropertyList
+ naturally
+ sysrandom (1.0.5)
+ terminal-notifier (2.0.0)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ trailblazer-option (0.1.2)
+ tty-cursor (0.7.1)
+ tty-screen (0.8.2)
+ tty-spinner (0.9.3)
+ tty-cursor (~> 0.7)
+ uber (0.1.0)
+ unicode-display_width (2.6.0)
+ word_wrap (1.0.0)
+ xcodeproj (1.27.0)
+ CFPropertyList (>= 2.3.3, < 4.0)
+ atomos (~> 0.1.3)
+ claide (>= 1.0.2, < 2.0)
+ colored2 (~> 3.1)
+ nanaimo (~> 0.4.0)
+ rexml (>= 3.3.6, < 4.0)
+ xcpretty (0.4.1)
+ rouge (~> 3.28.0)
+ xcpretty-travis-formatter (1.0.1)
+ xcpretty (~> 0.2, >= 0.0.7)
+
+PLATFORMS
+ arm64-darwin-23
+ ruby
+
+DEPENDENCIES
+ abbrev
+ fastlane
+
+BUNDLED WITH
+ 2.7.1
diff --git a/README.md b/README.md
index aba64eca23..ba1c3887be 100644
--- a/README.md
+++ b/README.md
@@ -170,3 +170,17 @@ flutter pub run flutter_launcher_icons
[Guided upgrade using Android Studio](https://docs.flutter.dev/release/breaking-changes/android-java-gradle-migration-guide#solution-1-guided-fix-using-android-studio)
[Java and Gradle compatibility](https://docs.gradle.org/current/userguide/compatibility.html)
+
+# Configuring fastlane
+
+Google service account
+
+```
+android/fastlane-service-account.json
+```
+
+Apple
+
+```
+ios/app-store-connect-api.p8
+```
diff --git a/android/Gemfile b/android/Gemfile
new file mode 100644
index 0000000000..fdf182dfa5
--- /dev/null
+++ b/android/Gemfile
@@ -0,0 +1,5 @@
+source "https://rubygems.org"
+
+gem "abbrev"
+gem "fastlane"
+gem "ostruct"
diff --git a/android/Gemfile.lock b/android/Gemfile.lock
new file mode 100644
index 0000000000..20e10f585d
--- /dev/null
+++ b/android/Gemfile.lock
@@ -0,0 +1,233 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ CFPropertyList (3.0.8)
+ abbrev (0.1.2)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
+ artifactory (3.0.17)
+ atomos (0.1.3)
+ aws-eventstream (1.4.0)
+ aws-partitions (1.1187.0)
+ aws-sdk-core (3.239.1)
+ aws-eventstream (~> 1, >= 1.3.0)
+ aws-partitions (~> 1, >= 1.992.0)
+ aws-sigv4 (~> 1.9)
+ base64
+ bigdecimal
+ jmespath (~> 1, >= 1.6.1)
+ logger
+ aws-sdk-kms (1.118.0)
+ aws-sdk-core (~> 3, >= 3.239.1)
+ aws-sigv4 (~> 1.5)
+ aws-sdk-s3 (1.205.0)
+ aws-sdk-core (~> 3, >= 3.234.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.5)
+ aws-sigv4 (1.12.1)
+ aws-eventstream (~> 1, >= 1.0.2)
+ babosa (1.0.4)
+ base64 (0.3.0)
+ bigdecimal (3.3.1)
+ claide (1.1.0)
+ colored (1.2)
+ colored2 (3.1.2)
+ commander (4.6.0)
+ highline (~> 2.0.0)
+ csv (3.3.5)
+ declarative (0.0.20)
+ digest-crc (0.7.0)
+ rake (>= 12.0.0, < 14.0.0)
+ domain_name (0.6.20240107)
+ dotenv (2.8.1)
+ emoji_regex (3.2.3)
+ excon (0.112.0)
+ faraday (1.10.4)
+ faraday-em_http (~> 1.0)
+ faraday-em_synchrony (~> 1.0)
+ faraday-excon (~> 1.1)
+ faraday-httpclient (~> 1.0)
+ faraday-multipart (~> 1.0)
+ faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.0)
+ faraday-patron (~> 1.0)
+ faraday-rack (~> 1.0)
+ faraday-retry (~> 1.0)
+ ruby2_keywords (>= 0.0.4)
+ faraday-cookie_jar (0.0.8)
+ faraday (>= 0.8.0)
+ http-cookie (>= 1.0.0)
+ faraday-em_http (1.0.0)
+ faraday-em_synchrony (1.0.1)
+ faraday-excon (1.1.0)
+ faraday-httpclient (1.0.1)
+ faraday-multipart (1.1.1)
+ multipart-post (~> 2.0)
+ faraday-net_http (1.0.2)
+ faraday-net_http_persistent (1.2.0)
+ faraday-patron (1.0.0)
+ faraday-rack (1.0.0)
+ faraday-retry (1.0.3)
+ faraday_middleware (1.2.1)
+ faraday (~> 1.0)
+ fastimage (2.4.0)
+ fastlane (2.229.0)
+ CFPropertyList (>= 2.3, < 4.0.0)
+ abbrev (~> 0.1.2)
+ addressable (>= 2.8, < 3.0.0)
+ artifactory (~> 3.0)
+ aws-sdk-s3 (~> 1.0)
+ babosa (>= 1.0.3, < 2.0.0)
+ bundler (>= 1.12.0, < 3.0.0)
+ colored (~> 1.2)
+ commander (~> 4.6)
+ csv (~> 3.3)
+ dotenv (>= 2.1.1, < 3.0.0)
+ emoji_regex (>= 0.1, < 4.0)
+ excon (>= 0.71.0, < 1.0.0)
+ faraday (~> 1.0)
+ faraday-cookie_jar (~> 0.0.6)
+ faraday_middleware (~> 1.0)
+ fastimage (>= 2.1.0, < 3.0.0)
+ fastlane-sirp (>= 1.0.0)
+ gh_inspector (>= 1.1.2, < 2.0.0)
+ google-apis-androidpublisher_v3 (~> 0.3)
+ google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-env (>= 1.6.0, < 2.0.0)
+ google-cloud-storage (~> 1.31)
+ highline (~> 2.0)
+ http-cookie (~> 1.0.5)
+ json (< 3.0.0)
+ jwt (>= 2.1.0, < 3)
+ mini_magick (>= 4.9.4, < 5.0.0)
+ multipart-post (>= 2.0.0, < 3.0.0)
+ mutex_m (~> 0.3.0)
+ naturally (~> 2.2)
+ optparse (>= 0.1.1, < 1.0.0)
+ plist (>= 3.1.0, < 4.0.0)
+ rubyzip (>= 2.0.0, < 3.0.0)
+ security (= 0.1.5)
+ simctl (~> 1.6.3)
+ terminal-notifier (>= 2.0.0, < 3.0.0)
+ terminal-table (~> 3)
+ tty-screen (>= 0.6.3, < 1.0.0)
+ tty-spinner (>= 0.8.0, < 1.0.0)
+ word_wrap (~> 1.0.0)
+ xcodeproj (>= 1.13.0, < 2.0.0)
+ xcpretty (~> 0.4.1)
+ xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
+ fastlane-sirp (1.0.0)
+ sysrandom (~> 1.0)
+ gh_inspector (1.1.3)
+ google-apis-androidpublisher_v3 (0.54.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-core (0.11.3)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (>= 0.16.2, < 2.a)
+ httpclient (>= 2.8.1, < 3.a)
+ mini_mime (~> 1.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.a)
+ rexml
+ google-apis-iamcredentials_v1 (0.17.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.13.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-storage_v1 (0.31.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-cloud-core (1.8.0)
+ google-cloud-env (>= 1.0, < 3.a)
+ google-cloud-errors (~> 1.0)
+ google-cloud-env (1.6.0)
+ faraday (>= 0.17.3, < 3.0)
+ google-cloud-errors (1.5.0)
+ google-cloud-storage (1.47.0)
+ addressable (~> 2.8)
+ digest-crc (~> 0.4)
+ google-apis-iamcredentials_v1 (~> 0.1)
+ google-apis-storage_v1 (~> 0.31.0)
+ google-cloud-core (~> 1.6)
+ googleauth (>= 0.16.2, < 2.a)
+ mini_mime (~> 1.0)
+ googleauth (1.8.1)
+ faraday (>= 0.17.3, < 3.a)
+ jwt (>= 1.4, < 3.0)
+ multi_json (~> 1.11)
+ os (>= 0.9, < 2.0)
+ signet (>= 0.16, < 2.a)
+ highline (2.0.3)
+ http-cookie (1.0.8)
+ domain_name (~> 0.5)
+ httpclient (2.9.0)
+ mutex_m
+ jmespath (1.6.2)
+ json (2.16.0)
+ jwt (2.10.2)
+ base64
+ logger (1.7.0)
+ mini_magick (4.13.2)
+ mini_mime (1.1.5)
+ multi_json (1.17.0)
+ multipart-post (2.4.1)
+ mutex_m (0.3.0)
+ nanaimo (0.4.0)
+ naturally (2.3.0)
+ optparse (0.8.0)
+ os (1.1.4)
+ ostruct (0.6.1)
+ plist (3.7.2)
+ public_suffix (6.0.2)
+ rake (13.3.1)
+ representable (3.2.0)
+ declarative (< 0.1.0)
+ trailblazer-option (>= 0.1.1, < 0.2.0)
+ uber (< 0.2.0)
+ retriable (3.1.2)
+ rexml (3.4.4)
+ rouge (3.28.0)
+ ruby2_keywords (0.0.5)
+ rubyzip (2.4.1)
+ security (0.1.5)
+ signet (0.21.0)
+ addressable (~> 2.8)
+ faraday (>= 0.17.5, < 3.a)
+ jwt (>= 1.5, < 4.0)
+ multi_json (~> 1.10)
+ simctl (1.6.10)
+ CFPropertyList
+ naturally
+ sysrandom (1.0.5)
+ terminal-notifier (2.0.0)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ trailblazer-option (0.1.2)
+ tty-cursor (0.7.1)
+ tty-screen (0.8.2)
+ tty-spinner (0.9.3)
+ tty-cursor (~> 0.7)
+ uber (0.1.0)
+ unicode-display_width (2.6.0)
+ word_wrap (1.0.0)
+ xcodeproj (1.27.0)
+ CFPropertyList (>= 2.3.3, < 4.0)
+ atomos (~> 0.1.3)
+ claide (>= 1.0.2, < 2.0)
+ colored2 (~> 3.1)
+ nanaimo (~> 0.4.0)
+ rexml (>= 3.3.6, < 4.0)
+ xcpretty (0.4.1)
+ rouge (~> 3.28.0)
+ xcpretty-travis-formatter (1.0.1)
+ xcpretty (~> 0.2, >= 0.0.7)
+
+PLATFORMS
+ arm64-darwin-23
+ ruby
+
+DEPENDENCIES
+ abbrev
+ fastlane
+ ostruct
+
+BUNDLED WITH
+ 2.7.1
diff --git a/android/app/MainActivity.template.kt b/android/app/MainActivity.template.kt
new file mode 100644
index 0000000000..65ef98d6fb
--- /dev/null
+++ b/android/app/MainActivity.template.kt
@@ -0,0 +1,12 @@
+package __PACKAGE__
+
+import androidx.annotation.NonNull
+import io.flutter.embedding.android.FlutterFragmentActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugins.GeneratedPluginRegistrant
+
+class MainActivity: FlutterFragmentActivity() {
+ override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+ GeneratedPluginRegistrant.registerWith(flutterEngine)
+ }
+}
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 344bbcf3bb..84554a9f1a 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -27,15 +27,56 @@ if (flutterVersionName == null) {
// App bundle signing
// https://docs.flutter.dev/deployment/android
def keystoreProperties = new Properties()
- def keystorePropertiesFile = rootProject.file('key.properties')
- if (keystorePropertiesFile.exists()) {
- keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
- }
+def keystorePropertiesFile = rootProject.file('key.properties')
+if (keystorePropertiesFile.exists()) {
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+}
+
+// Extract variables from Dart defines
+// Based on https://medium.com/@mahdi.yami3235/mastering-native-configurations-in-flutter-the-power-of-dart-define-10f2b89922dc
+def dartEnvironmentVariables = [];
+if (project.hasProperty('dart-defines')) {
+ dartEnvironmentVariables = project.property('dart-defines')
+ .split(',')
+ .collectEntries { entry ->
+ def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
+ [(pair.first()): pair.last()]
+ }
+}
+
+def appIdPrefix = dartEnvironmentVariables.APP_ID_PREFIX
+if (appIdPrefix == null) {
+ throw new GradleException("Dart define APP_ID_PREFIX not found.")
+}
+
+def appName = dartEnvironmentVariables.APP_NAME
+if (appName == null) {
+ throw new GradleException("Dart define APP_NAME not found.")
+}
+
+def generateMainActivityTask = tasks.register("generateMainActivity") {
+ doLast {
+ def packageName = "${appIdPrefix}.titan"
+
+ def templateFile = file("$projectDir/MainActivity.template.kt")
+ def packageDir = packageName.replace('.', '/')
+ def outputDir = file("$projectDir/src/main/kotlin/$packageDir")
+ outputDir.mkdirs()
+ def outputFile = new File(outputDir, "MainActivity.kt")
+
+ def content = templateFile.text
+ .replace("__PACKAGE__", packageName)
+
+ outputFile.text = content
+ }
+}
+
+preBuild.dependsOn(generateMainActivityTask)
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion "27.0.12077973"
- namespace "fr.myecl.titan"
+ namespace "${appIdPrefix}.titan"
compileOptions {
coreLibraryDesugaringEnabled true
@@ -44,7 +85,7 @@ android {
}
kotlinOptions {
- jvmTarget = '17'
+ jvmTarget = JavaVersion.VERSION_17.toString()
}
sourceSets {
@@ -52,7 +93,7 @@ android {
}
defaultConfig {
- applicationId "fr.myecl.titan"
+ applicationId "${appIdPrefix}.titan"
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
@@ -78,23 +119,23 @@ android {
productFlavors {
prod {
dimension "default"
- resValue "string", "app_name", "MyECL"
- resValue "string", "flavor_url_scheme", "titan"
- manifestPlaceholders = ['appAuthRedirectScheme': 'fr.myecl.titan']
+ resValue "string", "app_name", "${appName}"
+ resValue "string", "flavor_url_scheme", "${appIdPrefix}.titan"
+ manifestPlaceholders = ['appAuthRedirectScheme': "${appIdPrefix}.titan"]
}
alpha {
dimension "default"
- resValue "string", "app_name", "MyECL Alpha"
- resValue "string", "flavor_url_scheme", "titan.alpha"
+ resValue "string", "app_name", "${appName} Alpha"
+ resValue "string", "flavor_url_scheme", "${appIdPrefix}.titan.alpha"
applicationIdSuffix ".alpha"
- manifestPlaceholders = ['appAuthRedirectScheme': 'fr.myecl.titan.alpha']
+ manifestPlaceholders = ['appAuthRedirectScheme': "${appIdPrefix}.titan.alpha"]
}
dev {
dimension "default"
- resValue "string", "app_name", "MyECL Dev"
- resValue "string", "flavor_url_scheme", "titan.dev"
+ resValue "string", "app_name", "${appName} Dev"
+ resValue "string", "flavor_url_scheme", "${appIdPrefix}.titan.dev"
applicationIdSuffix ".dev"
- manifestPlaceholders = ['appAuthRedirectScheme': 'fr.myecl.titan.dev']
+ manifestPlaceholders = ['appAuthRedirectScheme': "${appIdPrefix}.titan.dev"]
}
}
}
@@ -110,6 +151,6 @@ dependencies {
android {
defaultConfig {
- minSdkVersion 23
+ minSdkVersion flutter.minSdkVersion
}
}
diff --git a/android/app/src/alpha/res/mipmap-hdpi/ic_launcher.png b/android/app/src/alpha/res/mipmap-hdpi/ic_launcher.png
index 5975719cc4..63fb07e2aa 100644
Binary files a/android/app/src/alpha/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/alpha/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/alpha/res/mipmap-mdpi/ic_launcher.png b/android/app/src/alpha/res/mipmap-mdpi/ic_launcher.png
index 12a4189321..e2afa718b5 100644
Binary files a/android/app/src/alpha/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/alpha/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/alpha/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/alpha/res/mipmap-xhdpi/ic_launcher.png
index 73c623abfc..0f426a68b3 100644
Binary files a/android/app/src/alpha/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/alpha/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/alpha/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/alpha/res/mipmap-xxhdpi/ic_launcher.png
index 447a4c4130..eeaa8c37cb 100644
Binary files a/android/app/src/alpha/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/alpha/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/alpha/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/alpha/res/mipmap-xxxhdpi/ic_launcher.png
index d7b0e582ad..81daad90ce 100644
Binary files a/android/app/src/alpha/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/alpha/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index 32465af703..ff47992dcc 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/android/app/src/dev/res/mipmap-hdpi/ic_launcher.png b/android/app/src/dev/res/mipmap-hdpi/ic_launcher.png
index 82f3ab495d..75307dacb7 100644
Binary files a/android/app/src/dev/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/dev/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/dev/res/mipmap-mdpi/ic_launcher.png b/android/app/src/dev/res/mipmap-mdpi/ic_launcher.png
index 596af2305d..9804166fd4 100644
Binary files a/android/app/src/dev/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/dev/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/dev/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/dev/res/mipmap-xhdpi/ic_launcher.png
index 05a5008b40..ae5df8c613 100644
Binary files a/android/app/src/dev/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/dev/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png
index c38790da64..f8df98fbed 100644
Binary files a/android/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/dev/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png
index c7c8088abc..466a0d1d20 100644
Binary files a/android/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/dev/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/emlyon/.gitkeep b/android/app/src/emlyon/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/android/app/src/emlyon/res/mipmap-hdpi/ic_launcher.png b/android/app/src/emlyon/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..5ebb4d61f2
Binary files /dev/null and b/android/app/src/emlyon/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/emlyon/res/mipmap-mdpi/ic_launcher.png b/android/app/src/emlyon/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..3cdca2df2b
Binary files /dev/null and b/android/app/src/emlyon/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/emlyon/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/emlyon/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..24f6299210
Binary files /dev/null and b/android/app/src/emlyon/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/emlyon/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/emlyon/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..e0effec346
Binary files /dev/null and b/android/app/src/emlyon/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/emlyon/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/emlyon/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..23cdb9551c
Binary files /dev/null and b/android/app/src/emlyon/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 05a8c3c7df..01775634a8 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -31,26 +31,22 @@
-
-
+
+
-
-
-
+ android:name="com.yalantis.ucrop.UCropActivity"
+ android:screenOrientation="portrait"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
+
+
diff --git a/android/app/src/main/kotlin/com/example/myecl/MainActivity.kt b/android/app/src/main/kotlin/fr/proximapp/myemapp/titan/MainActivity.kt
similarity index 91%
rename from android/app/src/main/kotlin/com/example/myecl/MainActivity.kt
rename to android/app/src/main/kotlin/fr/proximapp/myemapp/titan/MainActivity.kt
index 072c7de81f..76bd46e7e0 100644
--- a/android/app/src/main/kotlin/com/example/myecl/MainActivity.kt
+++ b/android/app/src/main/kotlin/fr/proximapp/myemapp/titan/MainActivity.kt
@@ -1,4 +1,4 @@
-package fr.myecl.titan
+package fr.proximapp.myemapp.titan
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterFragmentActivity
diff --git a/android/app/src/prod/res/mipmap-hdpi/ic_launcher.png b/android/app/src/prod/res/mipmap-hdpi/ic_launcher.png
index 197ffde8b0..5ebb4d61f2 100644
Binary files a/android/app/src/prod/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/prod/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/prod/res/mipmap-mdpi/ic_launcher.png b/android/app/src/prod/res/mipmap-mdpi/ic_launcher.png
index 203b7356c3..3cdca2df2b 100644
Binary files a/android/app/src/prod/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/prod/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/prod/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/prod/res/mipmap-xhdpi/ic_launcher.png
index 4393a1f940..24f6299210 100644
Binary files a/android/app/src/prod/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/prod/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/prod/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/prod/res/mipmap-xxhdpi/ic_launcher.png
index 32b36e3ae1..e0effec346 100644
Binary files a/android/app/src/prod/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/prod/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/prod/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/prod/res/mipmap-xxxhdpi/ic_launcher.png
index ed16a5f342..23cdb9551c 100644
Binary files a/android/app/src/prod/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/prod/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/fastlane/Appfile b/android/fastlane/Appfile
new file mode 100644
index 0000000000..a7c3256fe5
--- /dev/null
+++ b/android/fastlane/Appfile
@@ -0,0 +1,17 @@
+json_key_file("./fastlane-service-account.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
+package_name("fr.proximapp.myemapp.titan") # e.g. com.krausefx.app
+
+for_platform :android do
+ for_lane :beta do |options|
+ flavor = options[:flavor] # prod, alpha, dev
+ app_id_prefix = options[:prefix] # fr.myecl
+ app_name = options[:name] # MyECL
+
+ if flavor == "prod"
+ package_name("#{app_id_prefix}.titan")
+ else
+ package_name("#{app_id_prefix}.titan.#{flavor}")
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile
new file mode 100644
index 0000000000..e2541d4409
--- /dev/null
+++ b/android/fastlane/Fastfile
@@ -0,0 +1,33 @@
+# This file contains the fastlane.tools configuration
+# You can find the documentation at https://docs.fastlane.tools
+#
+# For a list of all available actions, check out
+#
+# https://docs.fastlane.tools/actions
+#
+# For a list of all available plugins, check out
+#
+# https://docs.fastlane.tools/plugins/available-plugins
+#
+
+# Uncomment the line if you want fastlane to automatically update itself
+# update_fastlane
+
+default_platform(:android)
+
+platform :android do
+
+ desc "Submit a new Beta Build to Google Play"
+ lane :beta do |options|
+ flavor = options[:flavor] # prod, alpha, dev
+
+ sh "flutter build appbundle --dart-define-from-file=config/config-#{flavor}.json --release --flavor='#{flavor}'"
+
+ upload_to_play_store(
+ track: 'internal',
+ aab: "../build/app/outputs/bundle/#{flavor}Release/app-#{flavor}-release.aab",
+ release_status: 'draft'
+ )
+
+ end
+end
diff --git a/android/fastlane/README.md b/android/fastlane/README.md
new file mode 100644
index 0000000000..64de184714
--- /dev/null
+++ b/android/fastlane/README.md
@@ -0,0 +1,32 @@
+fastlane documentation
+----
+
+# Installation
+
+Make sure you have the latest version of the Xcode command line tools installed:
+
+```sh
+xcode-select --install
+```
+
+For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
+
+# Available Actions
+
+## Android
+
+### android beta
+
+```sh
+[bundle exec] fastlane android beta
+```
+
+Submit a new Beta Build to Google Play
+
+----
+
+This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
+
+More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
+
+The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 90ead1df47..996471e57c 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Nov 09 22:02:36 CET 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/android/settings.gradle b/android/settings.gradle
index 3c7f977036..453df9d7b5 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.10.0' apply false
- id "org.jetbrains.kotlin.android" version "1.8.21" apply false
+ id "com.android.application" version '8.12.3' apply false
+ id "org.jetbrains.kotlin.android" version "2.2.21" apply false
id "com.google.gms.google-services" version "4.3.15" apply false
}
diff --git a/assets/emlyon/back.webp b/assets/emlyon/back.webp
new file mode 100644
index 0000000000..f5f2c9f43c
Binary files /dev/null and b/assets/emlyon/back.webp differ
diff --git a/assets/emlyon/icon.png b/assets/emlyon/icon.png
new file mode 100644
index 0000000000..1f02eec990
Binary files /dev/null and b/assets/emlyon/icon.png differ
diff --git a/assets/emlyon/login.webp b/assets/emlyon/login.webp
new file mode 100644
index 0000000000..2db1227c14
Binary files /dev/null and b/assets/emlyon/login.webp differ
diff --git a/assets/emlyon/logo.png b/assets/emlyon/logo.png
new file mode 100644
index 0000000000..cbe8e9cb56
Binary files /dev/null and b/assets/emlyon/logo.png differ
diff --git a/assets/images/icon_alpha.png b/assets/images/icon_alpha.png
index bd61be0b04..1a07d8b570 100644
Binary files a/assets/images/icon_alpha.png and b/assets/images/icon_alpha.png differ
diff --git a/assets/images/icon_prod.png b/assets/images/icon_prod.png
index 0809659300..1f02eec990 100644
Binary files a/assets/images/icon_prod.png and b/assets/images/icon_prod.png differ
diff --git a/assets/images/login.svg b/assets/images/login.svg
deleted file mode 100644
index 68fae2ebd5..0000000000
--- a/assets/images/login.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/assets/images/login.webp b/assets/images/login.webp
new file mode 100644
index 0000000000..37035d60a2
Binary files /dev/null and b/assets/images/login.webp differ
diff --git a/assets/images/logo_alpha.png b/assets/images/logo_alpha.png
index c715b0db13..f67c3318f7 100644
Binary files a/assets/images/logo_alpha.png and b/assets/images/logo_alpha.png differ
diff --git a/assets/images/logo_prod.png b/assets/images/logo_prod.png
index b39f1fefc4..cbe8e9cb56 100644
Binary files a/assets/images/logo_prod.png and b/assets/images/logo_prod.png differ
diff --git a/assets/images/proximapp.png b/assets/images/proximapp.png
new file mode 100644
index 0000000000..5410eefdd7
Binary files /dev/null and b/assets/images/proximapp.png differ
diff --git a/assets/images/vache.png b/assets/images/vache.png
new file mode 100644
index 0000000000..9c70f81a95
Binary files /dev/null and b/assets/images/vache.png differ
diff --git a/config/config-template.json b/config/config-template.json
new file mode 100644
index 0000000000..ccb88521fc
--- /dev/null
+++ b/config/config-template.json
@@ -0,0 +1,12 @@
+{
+ "flavor": "",
+ "TITAN_URL": "http://localhost:3000",
+ // A trailing slash is required for the BACKEND_HOST
+ "BACKEND_HOST": "",
+ "SCHOOL_NAME": "",
+ "PAYMENT_NAME": "",
+ "APP_ID_PREFIX": "",
+ "APP_NAME": "",
+ "PLAUSIBLE_HOST": "",
+ "PLAUSIBLE_DOMAIN": ""
+}
diff --git a/ios/Debug-Alpha.xcconfig b/ios/Debug-Alpha.xcconfig
new file mode 100644
index 0000000000..ea0003d84c
--- /dev/null
+++ b/ios/Debug-Alpha.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Debug.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.alpha
\ No newline at end of file
diff --git a/ios/Debug-Dev.xcconfig b/ios/Debug-Dev.xcconfig
new file mode 100644
index 0000000000..e20ac2964d
--- /dev/null
+++ b/ios/Debug-Dev.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Debug.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.dev
\ No newline at end of file
diff --git a/ios/Debug-Prod.xcconfig b/ios/Debug-Prod.xcconfig
new file mode 100644
index 0000000000..f5151758c8
--- /dev/null
+++ b/ios/Debug-Prod.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Debug.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan
\ No newline at end of file
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 7c56964006..1dc6cf7652 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/Flutter/Debug-Alpha.xcconfig b/ios/Flutter/Debug-Alpha.xcconfig
new file mode 100644
index 0000000000..ea0003d84c
--- /dev/null
+++ b/ios/Flutter/Debug-Alpha.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Debug.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.alpha
\ No newline at end of file
diff --git a/ios/Flutter/Debug-Dev.xcconfig b/ios/Flutter/Debug-Dev.xcconfig
new file mode 100644
index 0000000000..e20ac2964d
--- /dev/null
+++ b/ios/Flutter/Debug-Dev.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Debug.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.dev
\ No newline at end of file
diff --git a/ios/Flutter/Debug-Prod.xcconfig b/ios/Flutter/Debug-Prod.xcconfig
new file mode 100644
index 0000000000..f5151758c8
--- /dev/null
+++ b/ios/Flutter/Debug-Prod.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Debug.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan
\ No newline at end of file
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index ec97fc6f30..ea03e039c4 100644
--- a/ios/Flutter/Debug.xcconfig
+++ b/ios/Flutter/Debug.xcconfig
@@ -1,2 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
+#include "Flutter/Dart-Defines.xcconfig"
diff --git a/ios/Flutter/Release-Alpha.xcconfig b/ios/Flutter/Release-Alpha.xcconfig
new file mode 100644
index 0000000000..bdda8fe77c
--- /dev/null
+++ b/ios/Flutter/Release-Alpha.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Release.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.alpha
\ No newline at end of file
diff --git a/ios/Flutter/Release-Dev.xcconfig b/ios/Flutter/Release-Dev.xcconfig
new file mode 100644
index 0000000000..ba2e8d4e55
--- /dev/null
+++ b/ios/Flutter/Release-Dev.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Release.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.dev
\ No newline at end of file
diff --git a/ios/Flutter/Release-Prod.xcconfig b/ios/Flutter/Release-Prod.xcconfig
new file mode 100644
index 0000000000..e078ad4b7d
--- /dev/null
+++ b/ios/Flutter/Release-Prod.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Release.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan
\ No newline at end of file
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
index c4855bfe20..95f3c233df 100644
--- a/ios/Flutter/Release.xcconfig
+++ b/ios/Flutter/Release.xcconfig
@@ -1,2 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
+#include "Flutter/Dart-Defines.xcconfig"
\ No newline at end of file
diff --git a/ios/Flutter/extract_dart_defines.sh b/ios/Flutter/extract_dart_defines.sh
new file mode 100644
index 0000000000..47ae494d9f
--- /dev/null
+++ b/ios/Flutter/extract_dart_defines.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# This script extracts the Dart defines passed from Flutter and creates an
+# Xcode configuration file `Dart-Defines.xcconfig` which variables will
+# be accessible during the xcode compilation steps.
+# Based on https://medium.com/@mahdi.yami3235/mastering-native-configurations-in-flutter-the-power-of-dart-define-10f2b89922dc
+
+SRCROOT="${SRCROOT:-$(pwd)}"
+
+OUTPUT_FILE="${SRCROOT}/Flutter/Dart-Defines.xcconfig"
+: > $OUTPUT_FILE
+
+function decode_url() { echo "${*}" | base64 --decode; }
+
+IFS=',' read -r -a define_items <<<"$DART_DEFINES"
+
+for index in "${!define_items[@]}"
+do
+ item=$(decode_url "${define_items[$index]}")
+
+ lowercase_item=$(echo "$item" | tr '[:upper:]' '[:lower:]')
+ if [[ $lowercase_item != flutter* ]]; then
+ echo "$item" >> "$OUTPUT_FILE"
+ fi
+done
\ No newline at end of file
diff --git a/ios/Gemfile b/ios/Gemfile
new file mode 100644
index 0000000000..b5efb81871
--- /dev/null
+++ b/ios/Gemfile
@@ -0,0 +1,16 @@
+source "https://rubygems.org"
+
+gem "abbrev"
+
+
+gem "cocoapods", "1.16.2"
+gem "cocoapods-keys"
+gem "benchmark"
+
+gem "fastlane"
+gem "ostruct"
+
+gem "nkf"
+
+plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
+eval_gemfile(plugins_path) if File.exist?(plugins_path)
diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock
new file mode 100644
index 0000000000..31ca9de4b3
--- /dev/null
+++ b/ios/Gemfile.lock
@@ -0,0 +1,313 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ CFPropertyList (3.0.8)
+ abbrev (0.1.2)
+ activesupport (5.2.8.1)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
+ algoliasearch (1.27.5)
+ httpclient (~> 2.8, >= 2.8.3)
+ json (>= 1.5.1)
+ artifactory (3.0.17)
+ atomos (0.1.3)
+ aws-eventstream (1.4.0)
+ aws-partitions (1.1187.0)
+ aws-sdk-core (3.239.1)
+ aws-eventstream (~> 1, >= 1.3.0)
+ aws-partitions (~> 1, >= 1.992.0)
+ aws-sigv4 (~> 1.9)
+ base64
+ bigdecimal
+ jmespath (~> 1, >= 1.6.1)
+ logger
+ aws-sdk-kms (1.118.0)
+ aws-sdk-core (~> 3, >= 3.239.1)
+ aws-sigv4 (~> 1.5)
+ aws-sdk-s3 (1.205.0)
+ aws-sdk-core (~> 3, >= 3.234.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.5)
+ aws-sigv4 (1.12.1)
+ aws-eventstream (~> 1, >= 1.0.2)
+ babosa (1.0.4)
+ base64 (0.3.0)
+ benchmark (0.4.1)
+ bigdecimal (3.3.1)
+ claide (1.1.0)
+ cocoapods (1.16.2)
+ addressable (~> 2.8)
+ claide (>= 1.0.2, < 2.0)
+ cocoapods-core (= 1.16.2)
+ cocoapods-deintegrate (>= 1.0.3, < 2.0)
+ cocoapods-downloader (>= 2.1, < 3.0)
+ cocoapods-plugins (>= 1.0.0, < 2.0)
+ cocoapods-search (>= 1.0.0, < 2.0)
+ cocoapods-trunk (>= 1.6.0, < 2.0)
+ cocoapods-try (>= 1.1.0, < 2.0)
+ colored2 (~> 3.1)
+ escape (~> 0.0.4)
+ fourflusher (>= 2.3.0, < 3.0)
+ gh_inspector (~> 1.0)
+ molinillo (~> 0.8.0)
+ nap (~> 1.0)
+ ruby-macho (>= 2.3.0, < 3.0)
+ xcodeproj (>= 1.27.0, < 2.0)
+ cocoapods-core (1.16.2)
+ activesupport (>= 5.0, < 8)
+ addressable (~> 2.8)
+ algoliasearch (~> 1.0)
+ concurrent-ruby (~> 1.1)
+ fuzzy_match (~> 2.0.4)
+ nap (~> 1.0)
+ netrc (~> 0.11)
+ public_suffix (~> 4.0)
+ typhoeus (~> 1.0)
+ cocoapods-deintegrate (1.0.5)
+ cocoapods-downloader (2.1)
+ cocoapods-keys (2.3.1)
+ dotenv
+ ruby-keychain
+ cocoapods-plugins (1.0.0)
+ nap
+ cocoapods-search (1.0.1)
+ cocoapods-trunk (1.6.0)
+ nap (>= 0.8, < 2.0)
+ netrc (~> 0.11)
+ cocoapods-try (1.2.0)
+ colored (1.2)
+ colored2 (3.1.2)
+ commander (4.6.0)
+ highline (~> 2.0.0)
+ concurrent-ruby (1.3.5)
+ csv (3.3.5)
+ declarative (0.0.20)
+ digest-crc (0.7.0)
+ rake (>= 12.0.0, < 14.0.0)
+ domain_name (0.6.20240107)
+ dotenv (2.8.1)
+ emoji_regex (3.2.3)
+ escape (0.0.4)
+ ethon (0.16.0)
+ ffi (>= 1.15.0)
+ excon (0.112.0)
+ faraday (1.10.4)
+ faraday-em_http (~> 1.0)
+ faraday-em_synchrony (~> 1.0)
+ faraday-excon (~> 1.1)
+ faraday-httpclient (~> 1.0)
+ faraday-multipart (~> 1.0)
+ faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.0)
+ faraday-patron (~> 1.0)
+ faraday-rack (~> 1.0)
+ faraday-retry (~> 1.0)
+ ruby2_keywords (>= 0.0.4)
+ faraday-cookie_jar (0.0.8)
+ faraday (>= 0.8.0)
+ http-cookie (>= 1.0.0)
+ faraday-em_http (1.0.0)
+ faraday-em_synchrony (1.0.1)
+ faraday-excon (1.1.0)
+ faraday-httpclient (1.0.1)
+ faraday-multipart (1.1.1)
+ multipart-post (~> 2.0)
+ faraday-net_http (1.0.2)
+ faraday-net_http_persistent (1.2.0)
+ faraday-patron (1.0.0)
+ faraday-rack (1.0.0)
+ faraday-retry (1.0.3)
+ faraday_middleware (1.2.1)
+ faraday (~> 1.0)
+ fastimage (2.4.0)
+ fastlane (2.229.0)
+ CFPropertyList (>= 2.3, < 4.0.0)
+ abbrev (~> 0.1.2)
+ addressable (>= 2.8, < 3.0.0)
+ artifactory (~> 3.0)
+ aws-sdk-s3 (~> 1.0)
+ babosa (>= 1.0.3, < 2.0.0)
+ bundler (>= 1.12.0, < 3.0.0)
+ colored (~> 1.2)
+ commander (~> 4.6)
+ csv (~> 3.3)
+ dotenv (>= 2.1.1, < 3.0.0)
+ emoji_regex (>= 0.1, < 4.0)
+ excon (>= 0.71.0, < 1.0.0)
+ faraday (~> 1.0)
+ faraday-cookie_jar (~> 0.0.6)
+ faraday_middleware (~> 1.0)
+ fastimage (>= 2.1.0, < 3.0.0)
+ fastlane-sirp (>= 1.0.0)
+ gh_inspector (>= 1.1.2, < 2.0.0)
+ google-apis-androidpublisher_v3 (~> 0.3)
+ google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-env (>= 1.6.0, < 2.0.0)
+ google-cloud-storage (~> 1.31)
+ highline (~> 2.0)
+ http-cookie (~> 1.0.5)
+ json (< 3.0.0)
+ jwt (>= 2.1.0, < 3)
+ mini_magick (>= 4.9.4, < 5.0.0)
+ multipart-post (>= 2.0.0, < 3.0.0)
+ mutex_m (~> 0.3.0)
+ naturally (~> 2.2)
+ optparse (>= 0.1.1, < 1.0.0)
+ plist (>= 3.1.0, < 4.0.0)
+ rubyzip (>= 2.0.0, < 3.0.0)
+ security (= 0.1.5)
+ simctl (~> 1.6.3)
+ terminal-notifier (>= 2.0.0, < 3.0.0)
+ terminal-table (~> 3)
+ tty-screen (>= 0.6.3, < 1.0.0)
+ tty-spinner (>= 0.8.0, < 1.0.0)
+ word_wrap (~> 1.0.0)
+ xcodeproj (>= 1.13.0, < 2.0.0)
+ xcpretty (~> 0.4.1)
+ xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
+ fastlane-plugin-json (1.1.7)
+ fastlane-sirp (1.0.0)
+ sysrandom (~> 1.0)
+ ffi (1.17.2)
+ ffi (1.17.2-arm64-darwin)
+ fourflusher (2.3.1)
+ fuzzy_match (2.0.4)
+ gh_inspector (1.1.3)
+ google-apis-androidpublisher_v3 (0.54.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-core (0.11.3)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (>= 0.16.2, < 2.a)
+ httpclient (>= 2.8.1, < 3.a)
+ mini_mime (~> 1.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.a)
+ rexml
+ google-apis-iamcredentials_v1 (0.17.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.13.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-apis-storage_v1 (0.31.0)
+ google-apis-core (>= 0.11.0, < 2.a)
+ google-cloud-core (1.8.0)
+ google-cloud-env (>= 1.0, < 3.a)
+ google-cloud-errors (~> 1.0)
+ google-cloud-env (1.6.0)
+ faraday (>= 0.17.3, < 3.0)
+ google-cloud-errors (1.5.0)
+ google-cloud-storage (1.47.0)
+ addressable (~> 2.8)
+ digest-crc (~> 0.4)
+ google-apis-iamcredentials_v1 (~> 0.1)
+ google-apis-storage_v1 (~> 0.31.0)
+ google-cloud-core (~> 1.6)
+ googleauth (>= 0.16.2, < 2.a)
+ mini_mime (~> 1.0)
+ googleauth (1.8.1)
+ faraday (>= 0.17.3, < 3.a)
+ jwt (>= 1.4, < 3.0)
+ multi_json (~> 1.11)
+ os (>= 0.9, < 2.0)
+ signet (>= 0.16, < 2.a)
+ highline (2.0.3)
+ http-cookie (1.0.8)
+ domain_name (~> 0.5)
+ httpclient (2.9.0)
+ mutex_m
+ i18n (1.14.7)
+ concurrent-ruby (~> 1.0)
+ jmespath (1.6.2)
+ json (2.16.0)
+ jwt (2.10.2)
+ base64
+ logger (1.7.0)
+ mini_magick (4.13.2)
+ mini_mime (1.1.5)
+ minitest (5.25.5)
+ molinillo (0.8.0)
+ multi_json (1.17.0)
+ multipart-post (2.4.1)
+ mutex_m (0.3.0)
+ nanaimo (0.4.0)
+ nap (1.1.0)
+ naturally (2.3.0)
+ netrc (0.11.0)
+ nkf (0.2.0)
+ og-corefoundation (0.2.3)
+ ffi
+ optparse (0.8.0)
+ os (1.1.4)
+ ostruct (0.6.1)
+ plist (3.7.2)
+ public_suffix (4.0.7)
+ rake (13.3.1)
+ representable (3.2.0)
+ declarative (< 0.1.0)
+ trailblazer-option (>= 0.1.1, < 0.2.0)
+ uber (< 0.2.0)
+ retriable (3.1.2)
+ rexml (3.4.4)
+ rouge (3.28.0)
+ ruby-keychain (0.4.0)
+ ffi
+ og-corefoundation (~> 0.2.0)
+ ruby-macho (2.5.1)
+ ruby2_keywords (0.0.5)
+ rubyzip (2.4.1)
+ security (0.1.5)
+ signet (0.21.0)
+ addressable (~> 2.8)
+ faraday (>= 0.17.5, < 3.a)
+ jwt (>= 1.5, < 4.0)
+ multi_json (~> 1.10)
+ simctl (1.6.10)
+ CFPropertyList
+ naturally
+ sysrandom (1.0.5)
+ terminal-notifier (2.0.0)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ thread_safe (0.3.6)
+ trailblazer-option (0.1.2)
+ tty-cursor (0.7.1)
+ tty-screen (0.8.2)
+ tty-spinner (0.9.3)
+ tty-cursor (~> 0.7)
+ typhoeus (1.4.1)
+ ethon (>= 0.9.0)
+ tzinfo (1.2.11)
+ thread_safe (~> 0.1)
+ uber (0.1.0)
+ unicode-display_width (2.6.0)
+ word_wrap (1.0.0)
+ xcodeproj (1.27.0)
+ CFPropertyList (>= 2.3.3, < 4.0)
+ atomos (~> 0.1.3)
+ claide (>= 1.0.2, < 2.0)
+ colored2 (~> 3.1)
+ nanaimo (~> 0.4.0)
+ rexml (>= 3.3.6, < 4.0)
+ xcpretty (0.4.1)
+ rouge (~> 3.28.0)
+ xcpretty-travis-formatter (1.0.1)
+ xcpretty (~> 0.2, >= 0.0.7)
+
+PLATFORMS
+ arm64-darwin-23
+
+DEPENDENCIES
+ abbrev
+ benchmark
+ cocoapods (= 1.16.2)
+ cocoapods-keys
+ fastlane
+ fastlane-plugin-json
+ nkf
+ ostruct
+
+BUNDLED WITH
+ 2.7.1
diff --git a/ios/GoogleService-Info.plist b/ios/GoogleService-Info.plist
deleted file mode 100644
index 14ad19aa27..0000000000
--- a/ios/GoogleService-Info.plist
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
- CLIENT_ID
- 111651829257-sovttf6mt8fqjfeq8pi8v5hto7465ldv.apps.googleusercontent.com
- REVERSED_CLIENT_ID
- com.googleusercontent.apps.111651829257-sovttf6mt8fqjfeq8pi8v5hto7465ldv
- API_KEY
- AIzaSyCDW6mgU8-i4Ot-XB-bQLs_FsBjXQ74gY4
- GCM_SENDER_ID
- 111651829257
- PLIST_VERSION
- 1
- BUNDLE_ID
- fr.myecl.titan.alpha
- PROJECT_ID
- myecl-eclair
- STORAGE_BUCKET
- myecl-eclair.appspot.com
- IS_ADS_ENABLED
-
- IS_ANALYTICS_ENABLED
-
- IS_APPINVITE_ENABLED
-
- IS_GCM_ENABLED
-
- IS_SIGNIN_ENABLED
-
- GOOGLE_APP_ID
- 1:111651829257:ios:2584a091db1d9dd53f6ad8
-
-
\ No newline at end of file
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 1b61adda83..98b8b95e2a 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -89,16 +89,6 @@ PODS:
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- - GoogleMLKit/BarcodeScanning (7.0.0):
- - GoogleMLKit/MLKitCore
- - MLKitBarcodeScanning (~> 6.0.0)
- - GoogleMLKit/MLKitCore (7.0.0):
- - MLKitCommon (~> 12.0.0)
- - GoogleToolboxForMac/Defines (4.2.1)
- - GoogleToolboxForMac/Logger (4.2.1):
- - GoogleToolboxForMac/Defines (= 4.2.1)
- - "GoogleToolboxForMac/NSData+zlib (4.2.1)":
- - GoogleToolboxForMac/Defines (= 4.2.1)
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
@@ -123,7 +113,6 @@ PODS:
- GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- - GTMSessionFetcher/Core (3.5.0)
- image_cropper (0.0.4):
- Flutter
- TOCropViewController (~> 2.7.4)
@@ -132,26 +121,9 @@ PODS:
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
- - MLImage (1.0.0-beta6)
- - MLKitBarcodeScanning (6.0.0):
- - MLKitCommon (~> 12.0)
- - MLKitVision (~> 8.0)
- - MLKitCommon (12.0.0):
- - GoogleDataTransport (~> 10.0)
- - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
- - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
- - GoogleUtilities/Logger (~> 8.0)
- - GoogleUtilities/UserDefaults (~> 8.0)
- - GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
- - MLKitVision (8.0.0):
- - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
- - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
- - GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
- - MLImage (= 1.0.0-beta6)
- - MLKitCommon (~> 12.0)
- - mobile_scanner (6.0.2):
+ - mobile_scanner (7.0.0):
- Flutter
- - GoogleMLKit/BarcodeScanning (~> 7.0.0)
+ - FlutterMacOS
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
@@ -195,7 +167,7 @@ DEPENDENCIES:
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
+ - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- pdfx (from `.symlinks/plugins/pdfx/ios`)
@@ -215,14 +187,7 @@ SPEC REPOS:
- FirebaseInstallations
- FirebaseMessaging
- GoogleDataTransport
- - GoogleMLKit
- - GoogleToolboxForMac
- GoogleUtilities
- - GTMSessionFetcher
- - MLImage
- - MLKitBarcodeScanning
- - MLKitCommon
- - MLKitVision
- nanopb
- PromisesObjC
- SDWebImage
@@ -257,7 +222,7 @@ EXTERNAL SOURCES:
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
mobile_scanner:
- :path: ".symlinks/plugins/mobile_scanner/ios"
+ :path: ".symlinks/plugins/mobile_scanner/darwin"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@@ -288,23 +253,16 @@ SPEC CHECKSUMS:
FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679
FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3
FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9
- Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_appauth: d4abcf54856e5d8ba82ed7646ffc83245d4aa448
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
flutter_secure_storage_darwin: ce237a8775b39723566dc72571190a3769d70468
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
- GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318
- GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
- GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
- MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56
- MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2
- MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d
- MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e
- mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036
+ mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
diff --git a/ios/Release-Alpha.xcconfig b/ios/Release-Alpha.xcconfig
new file mode 100644
index 0000000000..bdda8fe77c
--- /dev/null
+++ b/ios/Release-Alpha.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Release.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.alpha
\ No newline at end of file
diff --git a/ios/Release-Dev.xcconfig b/ios/Release-Dev.xcconfig
new file mode 100644
index 0000000000..ba2e8d4e55
--- /dev/null
+++ b/ios/Release-Dev.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Release.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan.dev
\ No newline at end of file
diff --git a/ios/Release-Prod.xcconfig b/ios/Release-Prod.xcconfig
new file mode 100644
index 0000000000..e078ad4b7d
--- /dev/null
+++ b/ios/Release-Prod.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter/Release.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(APP_ID_PREFIX).titan
\ No newline at end of file
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index c021c407ab..c88b3c9e40 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -9,13 +9,18 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2E449DB12AAC978B002A62E0 /* config in Resources */ = {isa = PBXBuildFile; fileRef = 2E449DB02AAC978B002A62E0 /* config */; };
+ 2E7BE80D2E52386A0019074E /* Release-Prod.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2E7BE80C2E52386A0019074E /* Release-Prod.xcconfig */; };
+ 2E7BE80F2E5238710019074E /* Release-Alpha.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2E7BE80E2E5238710019074E /* Release-Alpha.xcconfig */; };
+ 2E7BE8112E5238800019074E /* Release-Dev.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2E7BE8102E5238800019074E /* Release-Dev.xcconfig */; };
+ 2E7BE8132E5238B50019074E /* Debug-Prod.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2E7BE8122E5238B50019074E /* Debug-Prod.xcconfig */; };
+ 2E7BE8152E5238BE0019074E /* Debug-Alpha.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2E7BE8142E5238BE0019074E /* Debug-Alpha.xcconfig */; };
+ 2E7BE8172E5238C60019074E /* Debug-Dev.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2E7BE8162E5238C60019074E /* Debug-Dev.xcconfig */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B48C9D5838D13816EADEEBE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 41DB4EFE6EC199B0BB0C20E7 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- E58A76F02CB6805B0024F420 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E58A76EF2CB6805B0024F420 /* GoogleService-Info.plist */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -37,6 +42,12 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
159AB80AD2B51C6D7D28AE76 /* Pods-Runner.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-prod.xcconfig"; sourceTree = ""; };
2E449DB02AAC978B002A62E0 /* config */ = {isa = PBXFileReference; lastKnownFileType = folder; path = config; sourceTree = ""; };
+ 2E7BE80C2E52386A0019074E /* Release-Prod.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Release-Prod.xcconfig"; sourceTree = ""; };
+ 2E7BE80E2E5238710019074E /* Release-Alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Release-Alpha.xcconfig"; sourceTree = ""; };
+ 2E7BE8102E5238800019074E /* Release-Dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Release-Dev.xcconfig"; sourceTree = ""; };
+ 2E7BE8122E5238B50019074E /* Debug-Prod.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Debug-Prod.xcconfig"; sourceTree = ""; };
+ 2E7BE8142E5238BE0019074E /* Debug-Alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Debug-Alpha.xcconfig"; sourceTree = ""; };
+ 2E7BE8162E5238C60019074E /* Debug-Dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Debug-Dev.xcconfig"; sourceTree = ""; };
2EB49A532937B564004F7D93 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
3EF737888DC7420CCB3C7E40 /* Pods-Runner.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-dev.xcconfig"; sourceTree = ""; };
@@ -60,7 +71,6 @@
CF600A22821ED74D8DCE4F05 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
D13F3C7F59A7D2FDD62D552A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
D971B68565D0F571B60A89AA /* Pods-Runner.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-alpha.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-alpha.xcconfig"; sourceTree = ""; };
- E58A76EF2CB6805B0024F420 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -89,13 +99,18 @@
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
- E58A76EF2CB6805B0024F420 /* GoogleService-Info.plist */,
2E449DB02AAC978B002A62E0 /* config */,
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
CF897D9A422151DDBD917267 /* Pods */,
BB1ED0B3ADEE5307F6A2B2DC /* Frameworks */,
+ 2E7BE80C2E52386A0019074E /* Release-Prod.xcconfig */,
+ 2E7BE80E2E5238710019074E /* Release-Alpha.xcconfig */,
+ 2E7BE8102E5238800019074E /* Release-Dev.xcconfig */,
+ 2E7BE8122E5238B50019074E /* Debug-Prod.xcconfig */,
+ 2E7BE8142E5238BE0019074E /* Debug-Alpha.xcconfig */,
+ 2E7BE8162E5238C60019074E /* Debug-Dev.xcconfig */,
);
sourceTree = "";
};
@@ -215,12 +230,17 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 2E7BE8112E5238800019074E /* Release-Dev.xcconfig in Resources */,
+ 2E7BE8152E5238BE0019074E /* Debug-Alpha.xcconfig in Resources */,
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 2E7BE80F2E5238710019074E /* Release-Alpha.xcconfig in Resources */,
+ 2E7BE8132E5238B50019074E /* Debug-Prod.xcconfig in Resources */,
+ 2E7BE8172E5238C60019074E /* Debug-Dev.xcconfig in Resources */,
+ 2E7BE80D2E52386A0019074E /* Release-Prod.xcconfig in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
2E449DB12AAC978B002A62E0 /* config in Resources */,
- E58A76F02CB6805B0024F420 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -408,7 +428,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 = "iphonesimulator iphoneos";
@@ -419,24 +439,22 @@
};
249021D4217E4FDB00AE95B9 /* Profile-Prod */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ baseConfigurationReference = 2E7BE80C2E52386A0019074E /* Release-Prod.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Prod";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = MyECL;
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan;
- PRODUCT_NAME = MyECL;
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -491,7 +509,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;
@@ -501,24 +519,22 @@
};
2E449DA32AAC86C2002A62E0 /* Debug-Dev */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ baseConfigurationReference = 2E7BE8162E5238C60019074E /* Debug-Dev.xcconfig */;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Dev";
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Dev;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan.dev;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "MyECL Dev";
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME) Dev";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan.dev;
- PRODUCT_NAME = "MyECL Dev";
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -568,7 +584,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 = "iphonesimulator iphoneos";
@@ -581,24 +597,22 @@
};
2E449DA52AAC86C9002A62E0 /* Release-Dev */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ baseConfigurationReference = 2E7BE8102E5238800019074E /* Release-Dev.xcconfig */;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Dev";
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Dev;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan.dev;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "MyECL Dev";
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME) Dev";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan.dev;
- PRODUCT_NAME = "MyECL Dev";
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -647,7 +661,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 = "iphonesimulator iphoneos";
@@ -658,24 +672,22 @@
};
2E449DA72AAC86CD002A62E0 /* Profile-Dev */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ baseConfigurationReference = 2E7BE8102E5238800019074E /* Release-Dev.xcconfig */;
buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Dev";
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Dev;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan.dev;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "MyECL Dev";
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME) Dev";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan.dev;
- PRODUCT_NAME = "MyECL Dev";
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -730,7 +742,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;
@@ -740,24 +752,22 @@
};
2E449DA92AAC874A002A62E0 /* Debug-Alpha */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ baseConfigurationReference = 2E7BE8142E5238BE0019074E /* Debug-Alpha.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Alpha";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan.alpha;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "MyECL Alpha";
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME) Alpha";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan.alpha;
- PRODUCT_NAME = "MyECL Alpha";
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -807,7 +817,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 = "iphonesimulator iphoneos";
@@ -820,24 +830,22 @@
};
2E449DAB2AAC8751002A62E0 /* Release-Alpha */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ baseConfigurationReference = 2E7BE80E2E5238710019074E /* Release-Alpha.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Alpha";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan.alpha;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "MyECL Alpha";
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME) Alpha";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan.alpha;
- PRODUCT_NAME = "MyECL Alpha";
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -886,7 +894,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 = "iphonesimulator iphoneos";
@@ -897,24 +905,22 @@
};
2E449DAD2AAC8759002A62E0 /* Profile-Alpha */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ baseConfigurationReference = 2E7BE80E2E5238710019074E /* Release-Alpha.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Alpha";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan.alpha;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "MyECL Alpha";
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME) Alpha";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan.alpha;
- PRODUCT_NAME = "MyECL Alpha";
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -969,7 +975,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;
@@ -1019,7 +1025,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 = "iphonesimulator iphoneos";
@@ -1032,24 +1038,22 @@
};
97C147061CF9000F007C117D /* Debug-Prod */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ baseConfigurationReference = 2E7BE8122E5238B50019074E /* Debug-Prod.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Prod";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = MyECL;
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan;
- PRODUCT_NAME = MyECL;
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -1059,24 +1063,22 @@
};
97C147071CF9000F007C117D /* Release-Prod */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ baseConfigurationReference = 2E7BE80C2E52386A0019074E /* Release-Prod.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Prod";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = D7YW2UB8DQ;
+ DEVELOPMENT_TEAM = 25XYVB6GDY;
ENABLE_BITCODE = NO;
- FLAVOR_URL_SCHEME = titan;
INFOPLIST_FILE = Runner/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = MyECL;
+ INFOPLIST_KEY_CFBundleDisplayName = "$(APP_NAME)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = fr.myecl.titan;
- PRODUCT_NAME = MyECL;
+ PRODUCT_NAME = "Titan";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Alpha.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Alpha.xcscheme
index ab7e0b354f..ba509abcdd 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Alpha.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Alpha.xcscheme
@@ -5,6 +5,24 @@
+
+
+
+
+
+
+
+
+
+
@@ -46,7 +64,7 @@
@@ -63,7 +81,7 @@
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Dev.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Dev.xcscheme
index 1456bcc6c7..a300e0b5f2 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Dev.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Dev.xcscheme
@@ -5,6 +5,24 @@
+
+
+
+
+
+
+
+
+
+
@@ -26,6 +44,7 @@
buildConfiguration = "Debug-Dev"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
@@ -33,6 +52,7 @@
buildConfiguration = "Release-Dev"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -44,7 +64,7 @@
@@ -61,7 +81,7 @@
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Prod.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Prod.xcscheme
index 0407e9828e..32325c03cd 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Prod.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Prod.xcscheme
@@ -1,10 +1,28 @@
+ version = "1.7">
+
+
+
+
+
+
+
+
+
+
@@ -26,12 +44,13 @@
buildConfiguration = "Debug-Prod"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
@@ -43,6 +62,7 @@
buildConfiguration = "Release-Prod"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -54,7 +74,7 @@
@@ -71,7 +91,7 @@
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-1024x1024@1x.png
index 9da7e707b2..9728b96ae7 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@1x.png
index 9441acdc4d..776b785d92 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@2x.png
index 6ab348d7a1..abbaf3a33b 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@3x.png
index acd17bdae9..adb5450e40 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@1x.png
index 010b95a6d2..d574f9f4cd 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@2x.png
index 5e616f5429..4407153000 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@3x.png
index d62f7743d5..277b8f7a0d 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@1x.png
index 6ab348d7a1..abbaf3a33b 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@2x.png
index 2ad49d6970..0e3e89423c 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@3x.png
index f1bef4db66..e5b151c4c3 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@1x.png
index ce9fb2de73..1f086f00b1 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@2x.png
index f172b0307d..c74349fc4a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-50x50@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@1x.png
index 0222086552..0da530438a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@2x.png
index 23a4db420d..9a7deb3e46 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-57x57@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@2x.png
index f1bef4db66..e5b151c4c3 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@3x.png
index 7051b1aa6b..72d0f76168 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@1x.png
index 5975719cc4..63fb07e2aa 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@2x.png
new file mode 100644
index 0000000000..eeaa8c37cb
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-72x72@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@1x.png
index d52c4119cf..112c01e9a7 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@2x.png
index aa508c504e..50d9014194 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-83.5x83.5@2x.png
index 322f24e97b..8d6efe8e9a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/AppIcon-Alpha-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/Contents.json
index f419b39775..a8ebb59775 100644
--- a/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/AppIcon-Alpha.appiconset/Contents.json
@@ -1 +1 @@
-{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Alpha-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Alpha-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Alpha-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Alpha-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Alpha-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Alpha-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Alpha-40x40@3x.png","scale":"3x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Alpha-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Alpha-50x50@2x.png","scale":"2x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Alpha-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Alpha-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Alpha-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Alpha-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Alpha-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Alpha-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Alpha-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Alpha-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Alpha-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Alpha-40x40@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Alpha-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Alpha-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Alpha-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Alpha-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Alpha-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Alpha-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
+{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Alpha-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Alpha-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Alpha-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Alpha-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Alpha-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Alpha-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Alpha-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Alpha-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Alpha-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Alpha-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Alpha-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Alpha-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Alpha-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Alpha-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Alpha-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Alpha-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Alpha-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Alpha-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Alpha-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Alpha-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Alpha-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Alpha-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Alpha-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Alpha-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Alpha-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-1024x1024@1x.png
index db9a28d2e5..9607d660dc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@1x.png
index bcc33fa7e1..a11aad9571 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@2x.png
index d1be804695..3c6ffbed38 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@3x.png
index 7b33868896..57df5e4cb6 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@1x.png
index cd5b5af6e3..7a49a9efba 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@2x.png
index 6b70d58d9a..16e70658fa 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@3x.png
index 6cb76a1c17..91fbfa36b2 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@1x.png
index d1be804695..3c6ffbed38 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@2x.png
index ad4f38aa71..1cecf0301e 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@3x.png
index dfea51f84c..31dfe916dd 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@1x.png
index 41d3696a90..374ffbdf15 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@2x.png
index 9f74da6214..238af1e686 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-50x50@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@1x.png
index f1bb563a3c..a2620843f8 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@2x.png
index 9fff825ff6..01f3f67464 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-57x57@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@2x.png
index dfea51f84c..31dfe916dd 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@3x.png
index 06e5e332b7..a9564a8950 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@1x.png
index 82f3ab495d..75307dacb7 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@2x.png
index c38790da64..f8df98fbed 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-72x72@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@1x.png
index df829f8755..d759903a18 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@2x.png
index 25b65d7f22..beb5427628 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-83.5x83.5@2x.png
index 17ca4a146d..b4f7957631 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/AppIcon-Dev-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/Contents.json
index f770de70ad..8534d59076 100644
--- a/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/AppIcon-Dev.appiconset/Contents.json
@@ -1 +1 @@
-{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Dev-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Dev-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Dev-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Dev-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Dev-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Dev-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Dev-40x40@3x.png","scale":"3x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Dev-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Dev-50x50@2x.png","scale":"2x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Dev-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Dev-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Dev-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Dev-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Dev-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Dev-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Dev-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Dev-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Dev-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Dev-40x40@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Dev-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Dev-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Dev-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Dev-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Dev-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Dev-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
+{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Dev-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Dev-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Dev-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Dev-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Dev-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Dev-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Dev-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Dev-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Dev-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Dev-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Dev-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Dev-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Dev-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Dev-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Dev-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Dev-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Dev-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Dev-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Dev-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Dev-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Dev-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Dev-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Dev-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Dev-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Dev-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-1024x1024@1x.png
index f4f4fb5e4c..05f2f251cc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@1x.png
index 7f6248337c..dace9d0d35 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@2x.png
index 38a0d15f3b..835eee837c 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@3x.png
index 9f6d6de02e..40b6cff004 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@1x.png
index 08d68e2795..deda39e026 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@2x.png
index 1d2ea2dea6..8df025c26d 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@3x.png
index d18a8fafbe..53b36fb2f6 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@1x.png
index 38a0d15f3b..835eee837c 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@2x.png
index e6bc469506..26c54e8fc6 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@3x.png
index 94cb19e728..add9a5fbbf 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@1x.png
index 33149ed81c..5dc6d040f5 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@2x.png
index 642ae41762..534a92bf73 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-50x50@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@1x.png
index 4710f642fc..401720c06a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@2x.png
index cdfcfacb81..3852ee3470 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-57x57@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@2x.png
index 94cb19e728..add9a5fbbf 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@3x.png
index cf93f35d31..81629613e9 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@1x.png
index 197ffde8b0..5ebb4d61f2 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@2x.png
index 32b36e3ae1..e0effec346 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-72x72@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@1x.png
index c255f12ceb..681bbe5e0c 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@2x.png
index 1db400b9ff..e74b6ab5a9 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-83.5x83.5@2x.png
index 3677ab02f3..993748f284 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/AppIcon-Prod-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/Contents.json
index 19cbfde4a6..3dede4dc9f 100644
--- a/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/AppIcon-Prod.appiconset/Contents.json
@@ -1 +1 @@
-{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Prod-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Prod-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Prod-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Prod-40x40@3x.png","scale":"3x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Prod-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Prod-50x50@2x.png","scale":"2x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Prod-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Prod-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Prod-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Prod-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Prod-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Prod-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Prod-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Prod-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Prod-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Prod-40x40@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Prod-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Prod-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Prod-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Prod-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Prod-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Prod-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
+{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Prod-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Prod-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Prod-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Prod-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Prod-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Prod-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Prod-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Prod-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Prod-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Prod-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Prod-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Prod-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Prod-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Prod-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Prod-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Prod-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Prod-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Prod-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Prod-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Prod-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Prod-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Prod-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-1024x1024@1x.png
new file mode 100644
index 0000000000..05f2f251cc
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@1x.png
new file mode 100644
index 0000000000..dace9d0d35
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@2x.png
new file mode 100644
index 0000000000..835eee837c
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@3x.png
new file mode 100644
index 0000000000..40b6cff004
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@1x.png
new file mode 100644
index 0000000000..deda39e026
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@2x.png
new file mode 100644
index 0000000000..8df025c26d
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@3x.png
new file mode 100644
index 0000000000..53b36fb2f6
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@1x.png
new file mode 100644
index 0000000000..835eee837c
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@2x.png
new file mode 100644
index 0000000000..26c54e8fc6
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@3x.png
new file mode 100644
index 0000000000..add9a5fbbf
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-50x50@1x.png
new file mode 100644
index 0000000000..5dc6d040f5
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-50x50@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-50x50@2x.png
new file mode 100644
index 0000000000..534a92bf73
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-50x50@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-57x57@1x.png
new file mode 100644
index 0000000000..401720c06a
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-57x57@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-57x57@2x.png
new file mode 100644
index 0000000000..3852ee3470
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-57x57@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-60x60@2x.png
new file mode 100644
index 0000000000..add9a5fbbf
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-60x60@3x.png
new file mode 100644
index 0000000000..81629613e9
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-72x72@1x.png
new file mode 100644
index 0000000000..5ebb4d61f2
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-72x72@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-72x72@2x.png
new file mode 100644
index 0000000000..e0effec346
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-72x72@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-76x76@1x.png
new file mode 100644
index 0000000000..681bbe5e0c
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-76x76@2x.png
new file mode 100644
index 0000000000..e74b6ab5a9
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-83.5x83.5@2x.png
new file mode 100644
index 0000000000..993748f284
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/AppIcon-Prod-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/Contents.json
new file mode 100644
index 0000000000..3dede4dc9f
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/AppIcon-emlyon.appiconset/Contents.json
@@ -0,0 +1 @@
+{"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-Prod-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-Prod-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-Prod-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Prod-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-Prod-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Prod-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-Prod-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Prod-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-Prod-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Prod-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-Prod-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Prod-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-Prod-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Prod-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-Prod-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Prod-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-Prod-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Prod-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-Prod-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Prod-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-Prod-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-Prod-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-Prod-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index bece8e5086..480d0b7317 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -6,8 +6,6 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
- CFBundleDisplayName
- $(PRODUCT_NAME)
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -15,7 +13,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- myecl
+ $(APP_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -28,10 +26,10 @@
CFBundleTypeRole
Editor
CFBundleURLName
- fr.myecl.titan
+ $(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleURLSchemes
- $(FLAVOR_URL_SCHEME)
+ $(PRODUCT_BUNDLE_IDENTIFIER)
diff --git a/ios/fastlane/Appfile b/ios/fastlane/Appfile
new file mode 100644
index 0000000000..f0253a88e8
--- /dev/null
+++ b/ios/fastlane/Appfile
@@ -0,0 +1,30 @@
+app_identifier("fr.proximapp.myemapp.titan") # The bundle identifier of your app
+apple_id("pnp@em-lyon.com") # Your Apple Developer Portal username
+
+itc_team_id("121183110") # App Store Connect Team ID
+team_id("25XYVB6GDY") # Developer Portal Team ID
+
+
+for_platform :ios do
+ for_lane :beta do |options|
+ flavor = options[:flavor] # prod, alpha, dev
+
+
+ config_json = read_json(
+ json_path: "./../config/config-#{flavor}.json"
+ )
+
+ app_id_prefix = config_json[:APP_ID_PREFIX] # fr.myecl
+ app_name = config_json[:APP_NAME] # MyECL
+
+ if flavor == "prod"
+ app_identifier("#{app_id_prefix}.titan")
+ else
+ app_identifier("#{app_id_prefix}.titan.#{flavor}")
+ end
+
+ end
+end
+
+# For more information about the Appfile, see:
+# https://docs.fastlane.tools/advanced/#appfile
diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile
new file mode 100644
index 0000000000..12a65c39df
--- /dev/null
+++ b/ios/fastlane/Fastfile
@@ -0,0 +1,46 @@
+# This file contains the fastlane.tools configuration
+# You can find the documentation at https://docs.fastlane.tools
+#
+# For a list of all available actions, check out
+#
+# https://docs.fastlane.tools/actions
+#
+# For a list of all available plugins, check out
+#
+# https://docs.fastlane.tools/plugins/available-plugins
+#
+
+# Uncomment the line if you want fastlane to automatically update itself
+# update_fastlane
+
+default_platform(:ios)
+
+platform :ios do
+ desc "Submit a new Beta Build to App Store"
+ lane :beta do |options|
+ flavor = options[:flavor] # prod, alpha, dev
+
+ config_json = read_json(
+ json_path: "./../config/config-#{flavor}.json"
+ )
+
+ app_id_prefix = config_json[:APP_ID_PREFIX] # fr.myecl
+ app_name = config_json[:APP_NAME] # MyECL
+
+ api_key = app_store_connect_api_key(
+ key_id: "LJN944T6G6",
+ issuer_id: "8f69d502-442d-459b-a1df-84a3cb7ca7b2",
+ key_filepath: "./app-store-connect-api.p8",
+ duration: 1200, # optional (maximum 1200)
+ in_house: false # optional but may be required if using match/sigh
+ )
+
+ sh "flutter build ipa --dart-define-from-file=config/config-#{flavor}.json --release --flavor='#{flavor}' --export-method=app-store"
+
+ upload_to_testflight(
+ ipa: "../build/ios/ipa/#{app_name}.ipa",
+ skip_waiting_for_build_processing: true,
+ api_key: api_key
+ )
+ end
+end
diff --git a/ios/fastlane/Pluginfile b/ios/fastlane/Pluginfile
new file mode 100644
index 0000000000..bffd8eb24d
--- /dev/null
+++ b/ios/fastlane/Pluginfile
@@ -0,0 +1,5 @@
+# Autogenerated by fastlane
+#
+# Ensure this file is checked in to source control!
+
+gem 'fastlane-plugin-json'
diff --git a/ios/fastlane/README.md b/ios/fastlane/README.md
new file mode 100644
index 0000000000..da1fda6ed0
--- /dev/null
+++ b/ios/fastlane/README.md
@@ -0,0 +1,32 @@
+fastlane documentation
+----
+
+# Installation
+
+Make sure you have the latest version of the Xcode command line tools installed:
+
+```sh
+xcode-select --install
+```
+
+For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
+
+# Available Actions
+
+## iOS
+
+### ios beta
+
+```sh
+[bundle exec] fastlane ios beta
+```
+
+Submit a new Beta Build to App Store
+
+----
+
+This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
+
+More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
+
+The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
diff --git a/l10n.yaml b/l10n.yaml
new file mode 100644
index 0000000000..c2f3c4ffd7
--- /dev/null
+++ b/l10n.yaml
@@ -0,0 +1,4 @@
+arb-dir: lib/l10n
+template-arb-file: app_fr.arb
+output-localization-file: app_localizations.dart
+untranslated-messages-file: missing.txt
diff --git a/lib/admin/admin.dart b/lib/admin/admin.dart
new file mode 100644
index 0000000000..b5bbd01d33
--- /dev/null
+++ b/lib/admin/admin.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+import 'package:titan/admin/router.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/ui/widgets/top_bar.dart';
+
+class AdminTemplate extends StatelessWidget {
+ final Widget child;
+ const AdminTemplate({super.key, required this.child});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ color: ColorConstants.background,
+ child: SafeArea(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ TopBar(root: AdminRouter.root),
+ Expanded(child: child),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/class/assocation.dart b/lib/admin/class/assocation.dart
new file mode 100644
index 0000000000..22efd6b521
--- /dev/null
+++ b/lib/admin/class/assocation.dart
@@ -0,0 +1,38 @@
+class Association {
+ Association({required this.name, required this.groupId, required this.id});
+ late final String name;
+ late final String groupId;
+ late final String id;
+
+ Association.fromJson(Map json) {
+ name = json['name'];
+ groupId = json['group_id'];
+ id = json['id'];
+ }
+
+ Map toJson() {
+ final data = {};
+ data['name'] = name;
+ data['group_id'] = groupId;
+ data['id'] = id;
+ return data;
+ }
+
+ Association copyWith({String? name, String? groupId, String? id}) =>
+ Association(
+ name: name ?? this.name,
+ groupId: groupId ?? this.groupId,
+ id: id ?? this.id,
+ );
+
+ Association.empty() {
+ name = 'Nom';
+ groupId = '';
+ id = '';
+ }
+
+ @override
+ String toString() {
+ return 'Association(name: $name, groupId: $groupId, id: $id)';
+ }
+}
diff --git a/lib/admin/providers/assocation_list_provider.dart b/lib/admin/providers/assocation_list_provider.dart
new file mode 100644
index 0000000000..6702b5abd0
--- /dev/null
+++ b/lib/admin/providers/assocation_list_provider.dart
@@ -0,0 +1,63 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/admin/class/assocation.dart';
+import 'package:titan/admin/repositories/association_repository.dart';
+import 'package:titan/tools/providers/list_notifier.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+
+class AssociationListNotifier extends ListNotifier {
+ final AssociationRepository associationRepository;
+ AssociationListNotifier({required this.associationRepository})
+ : super(const AsyncValue.loading());
+
+ Future>> loadAssociations() async {
+ return await loadList(associationRepository.getAssociationList);
+ }
+
+ Future createAssociation(Association association) async {
+ return await add(associationRepository.createAssociation, association);
+ }
+
+ Future updateAssociation(Association association) async {
+ return await update(
+ associationRepository.updateAssociation,
+ (associations, association) => associations
+ ..[associations.indexWhere((g) => g.id == association.id)] =
+ association,
+ association,
+ );
+ }
+
+ Future deleteAssociation(Association association) async {
+ return await delete(
+ associationRepository.deleteAssociation,
+ (associations, association) =>
+ associations..removeWhere((i) => i.id == association.id),
+ association.id,
+ association,
+ );
+ }
+
+ void setAssociation(Association association) {
+ state.whenData((d) {
+ if (d.indexWhere((g) => g.id == association.id) == -1) return;
+ state = AsyncValue.data(
+ d..[d.indexWhere((g) => g.id == association.id)] = association,
+ );
+ });
+ }
+}
+
+final associationListProvider =
+ StateNotifierProvider<
+ AssociationListNotifier,
+ AsyncValue>
+ >((ref) {
+ final associationRepository = ref.watch(associationRepositoryProvider);
+ AssociationListNotifier provider = AssociationListNotifier(
+ associationRepository: associationRepository,
+ );
+ tokenExpireWrapperAuth(ref, () async {
+ await provider.loadAssociations();
+ });
+ return provider;
+ });
diff --git a/lib/admin/providers/assocation_logo_provider.dart b/lib/admin/providers/assocation_logo_provider.dart
new file mode 100644
index 0000000000..1a0d895cd8
--- /dev/null
+++ b/lib/admin/providers/assocation_logo_provider.dart
@@ -0,0 +1,45 @@
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/association_logo_list.dart';
+import 'package:titan/admin/repositories/association_logo_repository.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/tools/providers/single_notifier.dart';
+
+class AssociationLogoNotifier extends SingleNotifier {
+ final associationLogoRepository = AssociationLogoRepository();
+ final AssociationLogoListNotifier associationLogoListNotifier;
+ AssociationLogoNotifier({
+ required String token,
+ required this.associationLogoListNotifier,
+ }) : super(const AsyncValue.loading()) {
+ associationLogoRepository.setToken(token);
+ }
+
+ Future getAssociationLogo(String id) async {
+ final image = await associationLogoRepository.getAssociationLogo(id);
+ associationLogoListNotifier.setTData(id, AsyncData([image]));
+ return image;
+ }
+
+ Future updateAssociationLogo(String id, Uint8List bytes) async {
+ associationLogoListNotifier.setTData(id, const AsyncLoading());
+ final image = await associationLogoRepository.addAssociationLogo(bytes, id);
+ associationLogoListNotifier.setTData(id, AsyncData([image]));
+ return image;
+ }
+}
+
+final associationLogoProvider =
+ StateNotifierProvider>((ref) {
+ final token = ref.watch(tokenProvider);
+ final associationLogoListNotifier = ref.watch(
+ associationLogoListProvider.notifier,
+ );
+ return AssociationLogoNotifier(
+ token: token,
+ associationLogoListNotifier: associationLogoListNotifier,
+ );
+ });
diff --git a/lib/admin/providers/association_logo_list.dart b/lib/admin/providers/association_logo_list.dart
new file mode 100644
index 0000000000..0ac30face6
--- /dev/null
+++ b/lib/admin/providers/association_logo_list.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/tools/providers/map_provider.dart';
+
+class AssociationLogoListNotifier extends MapNotifier {
+ AssociationLogoListNotifier() : super();
+}
+
+final associationLogoListProvider =
+ StateNotifierProvider<
+ AssociationLogoListNotifier,
+ Map>?>
+ >((ref) {
+ AssociationLogoListNotifier associationLogoNotifier =
+ AssociationLogoListNotifier();
+ return associationLogoNotifier;
+ });
diff --git a/lib/admin/providers/association_logo_provider.dart b/lib/admin/providers/association_logo_provider.dart
new file mode 100644
index 0000000000..5dd5038e2b
--- /dev/null
+++ b/lib/admin/providers/association_logo_provider.dart
@@ -0,0 +1,63 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:image_picker/image_picker.dart';
+import 'package:titan/admin/providers/associations_logo_map_provider.dart';
+import 'package:titan/admin/repositories/association_logo_repository.dart';
+import 'package:titan/tools/providers/single_notifier.dart';
+
+class AssociationLogoProvider extends SingleNotifier {
+ final AssociationLogoRepository associationLogoRepository;
+ final AssociationLogoMapNotifier associationLogoMapNotifier;
+ final ImagePicker _picker = ImagePicker();
+
+ AssociationLogoProvider({
+ required this.associationLogoRepository,
+ required this.associationLogoMapNotifier,
+ }) : super(const AsyncLoading());
+
+ Future getAssociationLogo(String associationId) async {
+ final image = await associationLogoRepository.getAssociationLogo(
+ associationId,
+ );
+ associationLogoMapNotifier.setTData(associationId, AsyncData([image]));
+ state = AsyncData(image);
+ return image;
+ }
+
+ Future setLogo(ImageSource source, String associationId) async {
+ final previousState = state;
+ state = const AsyncLoading();
+ final XFile? image = await _picker.pickImage(
+ source: source,
+ imageQuality: 20,
+ );
+ if (image != null) {
+ try {
+ final i = await associationLogoRepository.addAssociationLogo(
+ await image.readAsBytes(),
+ associationId,
+ );
+ state = AsyncValue.data(i);
+ associationLogoMapNotifier.setTData(associationId, AsyncData([i]));
+ return true;
+ } catch (e) {
+ state = previousState;
+ return false;
+ }
+ }
+ state = previousState;
+ return null;
+ }
+}
+
+final associationLogoProvider =
+ StateNotifierProvider>((ref) {
+ final associationLogo = ref.watch(associationLogoRepository);
+ final sessionPosterMapNotifier = ref.watch(
+ associationLogoMapProvider.notifier,
+ );
+ return AssociationLogoProvider(
+ associationLogoRepository: associationLogo,
+ associationLogoMapNotifier: sessionPosterMapNotifier,
+ );
+ });
diff --git a/lib/admin/providers/associations_logo_map_provider.dart b/lib/admin/providers/associations_logo_map_provider.dart
new file mode 100644
index 0000000000..504052649c
--- /dev/null
+++ b/lib/admin/providers/associations_logo_map_provider.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/tools/providers/map_provider.dart';
+
+class AssociationLogoMapNotifier extends MapNotifier {
+ AssociationLogoMapNotifier() : super();
+}
+
+final associationLogoMapProvider =
+ StateNotifierProvider<
+ AssociationLogoMapNotifier,
+ Map>?>
+ >((ref) {
+ AssociationLogoMapNotifier associationLogoNotifier =
+ AssociationLogoMapNotifier();
+ return associationLogoNotifier;
+ });
diff --git a/lib/admin/providers/group_from_simple_group_provider.dart b/lib/admin/providers/group_from_simple_group_provider.dart
new file mode 100644
index 0000000000..2beac831dc
--- /dev/null
+++ b/lib/admin/providers/group_from_simple_group_provider.dart
@@ -0,0 +1,27 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/admin/class/group.dart';
+import 'package:titan/admin/providers/group_list_provider.dart';
+import 'package:titan/tools/providers/single_map_provider.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+
+class GroupFromSimpleGroupNotifier extends SingleMapNotifier {
+ GroupFromSimpleGroupNotifier() : super();
+}
+
+final groupFromSimpleGroupProvider =
+ StateNotifierProvider<
+ GroupFromSimpleGroupNotifier,
+ Map?>
+ >((ref) {
+ GroupFromSimpleGroupNotifier groupFromSimpleGroupNotifier =
+ GroupFromSimpleGroupNotifier();
+ tokenExpireWrapperAuth(ref, () async {
+ final simpleGroups = ref.watch(allGroupListProvider);
+ simpleGroups.whenData((value) {
+ groupFromSimpleGroupNotifier.loadTList(
+ value.map((e) => e.id).toList(),
+ );
+ });
+ });
+ return groupFromSimpleGroupNotifier;
+ });
diff --git a/lib/admin/providers/my_association_list_provider.dart b/lib/admin/providers/my_association_list_provider.dart
new file mode 100644
index 0000000000..1c269ff4d3
--- /dev/null
+++ b/lib/admin/providers/my_association_list_provider.dart
@@ -0,0 +1,38 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/admin/class/assocation.dart';
+import 'package:titan/admin/repositories/association_repository.dart';
+import 'package:titan/tools/providers/list_notifier.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+
+class MyAssociationListNotifier extends ListNotifier {
+ final AssociationRepository associationRepository;
+ MyAssociationListNotifier({required this.associationRepository})
+ : super(const AsyncValue.loading());
+
+ Future>> loadAssociations() async {
+ return await loadList(associationRepository.getMyAssociations);
+ }
+}
+
+final asyncMyAssociationListProvider =
+ StateNotifierProvider<
+ MyAssociationListNotifier,
+ AsyncValue>
+ >((ref) {
+ final associationRepository = ref.watch(associationRepositoryProvider);
+ MyAssociationListNotifier provider = MyAssociationListNotifier(
+ associationRepository: associationRepository,
+ );
+ tokenExpireWrapperAuth(ref, () async {
+ await provider.loadAssociations();
+ });
+ return provider;
+ });
+
+final myAssociationListProvider = Provider>((ref) {
+ final asyncMyAssociationList = ref.watch(asyncMyAssociationListProvider);
+ return asyncMyAssociationList.maybeWhen(
+ data: (associations) => associations,
+ orElse: () => [],
+ );
+});
diff --git a/lib/admin/providers/school_id_provider.dart b/lib/admin/providers/school_id_provider.dart
deleted file mode 100644
index 306cf6d71f..0000000000
--- a/lib/admin/providers/school_id_provider.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-
-class SchoolIdNotifier extends StateNotifier {
- SchoolIdNotifier() : super("");
-
- void setId(String id) {
- state = id;
- }
-}
-
-final schoolIdProvider = StateNotifierProvider(
- (ref) => SchoolIdNotifier(),
-);
diff --git a/lib/admin/providers/structure_manager_provider.dart b/lib/admin/providers/structure_manager_provider.dart
index 7153b20c5b..95fa9ea654 100644
--- a/lib/admin/providers/structure_manager_provider.dart
+++ b/lib/admin/providers/structure_manager_provider.dart
@@ -4,8 +4,8 @@ import 'package:titan/user/class/simple_users.dart';
class StructureManagerProvider extends StateNotifier {
StructureManagerProvider() : super(SimpleUser.empty());
- void setUser(SimpleUser id) {
- state = id;
+ void setUser(SimpleUser user) {
+ state = user;
}
}
diff --git a/lib/admin/providers/structure_provider.dart b/lib/admin/providers/structure_provider.dart
index d678a7bc75..1cd9308633 100644
--- a/lib/admin/providers/structure_provider.dart
+++ b/lib/admin/providers/structure_provider.dart
@@ -7,6 +7,10 @@ class StructureNotifier extends StateNotifier {
void setStructure(Structure structure) {
state = structure;
}
+
+ void resetStructure() {
+ state = Structure.empty();
+ }
}
final structureProvider = StateNotifierProvider(
diff --git a/lib/admin/providers/user_association_membership_list_provider.dart b/lib/admin/providers/user_association_membership_list_provider.dart
deleted file mode 100644
index 9a6ad7875a..0000000000
--- a/lib/admin/providers/user_association_membership_list_provider.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/user_association_membership.dart';
-import 'package:titan/admin/repositories/association_membership_user_repository.dart';
-import 'package:titan/tools/providers/list_notifier.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-
-class UserMembershiplistNotifier
- extends ListNotifier {
- final AssociationMembershipUserRepository associationMembershipUserRepository;
- UserMembershiplistNotifier({
- required this.associationMembershipUserRepository,
- }) : super(const AsyncValue.loading());
-
- Future>>
- loadPersonalAssociationMembershipsList() async {
- return await loadList(
- () async => associationMembershipUserRepository
- .getPersonalAssociationMembershipList(),
- );
- }
-
- Future>>
- loadUserAssociationMembershipsList(String userId) async {
- return await loadList(
- () async => associationMembershipUserRepository
- .getUserAssociationMembershipList(userId),
- );
- }
-}
-
-final userMembershipListProvider =
- StateNotifierProvider<
- UserMembershiplistNotifier,
- AsyncValue>
- >((ref) {
- final associationMembershipUserRepository = ref.watch(
- associationMembershipUserRepositoryProvider,
- );
- return UserMembershiplistNotifier(
- associationMembershipUserRepository:
- associationMembershipUserRepository,
- );
- });
-
-final myMembershipListProvider =
- StateNotifierProvider<
- UserMembershiplistNotifier,
- AsyncValue>
- >((ref) {
- final associationMembershipUserRepository = ref.watch(
- associationMembershipUserRepositoryProvider,
- );
- UserMembershiplistNotifier provider = UserMembershiplistNotifier(
- associationMembershipUserRepository:
- associationMembershipUserRepository,
- );
- tokenExpireWrapperAuth(ref, () async {
- await provider.loadPersonalAssociationMembershipsList();
- });
- return provider;
- });
diff --git a/lib/admin/providers/user_invitation_provider.dart b/lib/admin/providers/user_invitation_provider.dart
new file mode 100644
index 0000000000..d750b71d24
--- /dev/null
+++ b/lib/admin/providers/user_invitation_provider.dart
@@ -0,0 +1,25 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/repositories/user_invitation_repository.dart';
+
+class UserInvitationNotifier extends StateNotifier {
+ final UserInvitationRepository userInvitationRepository;
+ UserInvitationNotifier({required this.userInvitationRepository})
+ : super(null);
+
+ Future> createUsers(
+ List mailList,
+ String? groupId,
+ ) async {
+ return await userInvitationRepository.createUsers(mailList, groupId);
+ }
+}
+
+final userInvitationProvider =
+ StateNotifierProvider((ref) {
+ final userInvitationRepository = ref.watch(
+ userInvitationRepositoryProvider,
+ );
+ return UserInvitationNotifier(
+ userInvitationRepository: userInvitationRepository,
+ );
+ });
diff --git a/lib/admin/repositories/association_logo_repository.dart b/lib/admin/repositories/association_logo_repository.dart
new file mode 100644
index 0000000000..dd3111aa49
--- /dev/null
+++ b/lib/admin/repositories/association_logo_repository.dart
@@ -0,0 +1,31 @@
+import 'dart:async';
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/tools/repository/logo_repository.dart';
+
+class AssociationLogoRepository extends LogoRepository {
+ @override
+ // ignore: overridden_fields
+ final ext = "associations/";
+
+ Future getAssociationLogo(String id) async {
+ final uint8List = await getLogo("", suffix: "$id/logo");
+ if (uint8List.isEmpty) {
+ return Image.asset("assets/images/vache.png", fit: BoxFit.cover);
+ }
+ return Image.memory(uint8List);
+ }
+
+ Future addAssociationLogo(Uint8List bytes, String id) async {
+ final uint8List = await addLogo(bytes, "", suffix: "$id/logo");
+ return Image.memory(uint8List);
+ }
+}
+
+final associationLogoRepository = Provider((ref) {
+ final token = ref.watch(tokenProvider);
+ return AssociationLogoRepository()..setToken(token);
+});
diff --git a/lib/admin/repositories/association_repository.dart b/lib/admin/repositories/association_repository.dart
new file mode 100644
index 0000000000..aa24f9fcca
--- /dev/null
+++ b/lib/admin/repositories/association_repository.dart
@@ -0,0 +1,39 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/admin/class/assocation.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/tools/repository/repository.dart';
+
+class AssociationRepository extends Repository {
+ @override
+ // ignore: overridden_fields
+ final ext = "associations/";
+
+ Future> getAssociationList() async {
+ return List.from(
+ (await getList()).map((x) => Association.fromJson(x)),
+ );
+ }
+
+ Future> getMyAssociations() async {
+ return List.from(
+ (await getList(suffix: "me")).map((x) => Association.fromJson(x)),
+ );
+ }
+
+ Future deleteAssociation(String associationId) async {
+ return await delete(associationId);
+ }
+
+ Future updateAssociation(Association association) async {
+ return await update(association.toJson(), association.id);
+ }
+
+ Future createAssociation(Association association) async {
+ return Association.fromJson(await create(association.toJson()));
+ }
+}
+
+final associationRepositoryProvider = Provider((ref) {
+ final token = ref.watch(tokenProvider);
+ return AssociationRepository()..setToken(token);
+});
diff --git a/lib/admin/repositories/notification_repository.dart b/lib/admin/repositories/notification_repository.dart
new file mode 100644
index 0000000000..e831bc5c51
--- /dev/null
+++ b/lib/admin/repositories/notification_repository.dart
@@ -0,0 +1,26 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/tools/repository/repository.dart';
+
+class NotificationRepository extends Repository {
+ @override
+ // ignore: overridden_fields
+ final ext = "notification/";
+
+ Future sendNotification(
+ String groupId,
+ String title,
+ String content,
+ ) async {
+ return await create({
+ "group_id": groupId,
+ "title": title,
+ "content": content,
+ }, suffix: "send");
+ }
+}
+
+final notificationRepositoryProvider = Provider((ref) {
+ final token = ref.watch(tokenProvider);
+ return NotificationRepository()..setToken(token);
+});
diff --git a/lib/admin/repositories/user_invitation_repository.dart b/lib/admin/repositories/user_invitation_repository.dart
new file mode 100644
index 0000000000..f19e0f4de4
--- /dev/null
+++ b/lib/admin/repositories/user_invitation_repository.dart
@@ -0,0 +1,29 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/tools/repository/repository.dart';
+
+class UserInvitationRepository extends Repository {
+ @override
+ // ignore: overridden_fields
+ final ext = "users/";
+
+ Future> createUsers(
+ List mailList,
+ String? groupId,
+ ) async {
+ final json = mailList
+ .map((email) => {'email': email, "default_group_id": groupId})
+ .toList();
+ final result = (await create(json, suffix: "batch-invitation"))["failed"];
+ List failedEmails = [];
+ for (var entry in result.entries) {
+ if (entry.value != "User already invited") failedEmails.add(entry.key);
+ }
+ return failedEmails;
+ }
+}
+
+final userInvitationRepositoryProvider = Provider((ref) {
+ final token = ref.watch(tokenProvider);
+ return UserInvitationRepository()..setToken(token);
+});
diff --git a/lib/admin/router.dart b/lib/admin/router.dart
index b98e20e3e0..95fa2cbafb 100644
--- a/lib/admin/router.dart
+++ b/lib/admin/router.dart
@@ -1,33 +1,30 @@
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:titan/admin/providers/is_admin_provider.dart';
-import 'package:titan/admin/ui/pages/groups/add_group_page/add_group_page.dart'
- deferred as add_group_page;
-import 'package:titan/admin/ui/pages/groups/add_loaner_page/add_loaner_page.dart'
- deferred as add_loaner_page;
-import 'package:titan/admin/ui/pages/edit_module_visibility/edit_module_visibility.dart'
- deferred as edit_module_visibility;
import 'package:titan/admin/ui/pages/groups/edit_group_page/edit_group_page.dart'
deferred as edit_group_page;
-import 'package:titan/admin/ui/pages/groups/group_page/group_page.dart'
- deferred as group_page;
-import 'package:titan/admin/ui/pages/memberships/add_edit_user_membership_page/add_edit_user_membership_page.dart'
- deferred as add_edit_user_membership_page;
-import 'package:titan/admin/ui/pages/memberships/association_membership_detail_page/association_membership_detail_page.dart'
- deferred as association_membership_detail_page;
-import 'package:titan/admin/ui/pages/memberships/association_membership_page/association_membership_page.dart'
- deferred as association_membership_page;
-import 'package:titan/admin/ui/pages/schools/school_page/school_page.dart'
- deferred as school_page;
-import 'package:titan/admin/ui/pages/schools/add_school_page/add_school_page.dart'
- deferred as add_school_page;
-import 'package:titan/admin/ui/pages/schools/edit_school_page/edit_school_page.dart'
- deferred as edit_school_page;
-import 'package:titan/admin/ui/pages/structure_page/structure_page.dart'
- deferred as structure_page;
-import 'package:titan/admin/ui/pages/add_edit_structure_page/add_edit_structure_page.dart'
- deferred as add_edit_structure_page;
import 'package:titan/admin/ui/pages/main_page/main_page.dart'
deferred as main_page;
+import 'package:titan/admin/ui/pages/groups/groups_page/groups_page.dart'
+ deferred as groups_page;
+import 'package:titan/admin/ui/pages/users_management_page/users_management_page.dart'
+ deferred as users_managmement_page;
+import 'package:titan/admin/ui/pages/group_notifification_page/group_notification_page.dart'
+ deferred as group_notification_page;
+import 'package:titan/admin/ui/pages/structure_page/add_edit_structure_page/add_edit_structure_page.dart'
+ deferred as add_edit_structure_page;
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
+import 'package:titan/admin/ui/pages/structure_page/structure_page.dart'
+ deferred as structure_page;
+import 'package:titan/admin/ui/pages/membership/association_membership_page/association_membership_page.dart'
+ deferred as association_membership_page;
+import 'package:titan/admin/ui/pages/membership/association_membership_detail_page/association_membership_detail_page.dart'
+ deferred as association_membership_detail_page;
+import 'package:titan/admin/ui/pages/membership/add_edit_user_membership_page/add_edit_user_membership_page.dart'
+ deferred as add_edit_user_membership_page;
+import 'package:titan/admin/ui/pages/association_page/association_page.dart'
+ deferred as association_page;
import 'package:titan/tools/middlewares/admin_middleware.dart';
import 'package:titan/tools/middlewares/authenticated_middleware.dart';
import 'package:titan/tools/middlewares/deferred_middleware.dart';
@@ -35,21 +32,26 @@ import 'package:qlevar_router/qlevar_router.dart';
class AdminRouter {
final Ref ref;
+ static const String structures = '/structures';
+ static const String addEditStructure = '/add_edit_structure';
static const String root = '/admin';
- static const String groups = '/groups';
+ static const String usersManagement = '/users_management';
+ static const String usersGroups = '/users_groups';
+ static const String groupNotification = '/group_notification';
static const String addGroup = '/add_group';
static const String editGroup = '/edit_group';
- static const String addLoaner = '/add_loaner';
- static const String schools = '/schools';
- static const String addSchool = '/add_school';
- static const String editSchool = '/edit_school';
- static const String structures = '/structures';
- static const String addEditStructure = '/add_edit_structure';
- static const String editModuleVisibility = '/edit_module_visibility';
static const String associationMemberships = '/association_memberships';
static const String detailAssociationMembership =
'/detail_association_membership';
static const String addEditMember = '/add_edit_member';
+ static const String association = '/association';
+ static final Module module = Module(
+ getName: (context) => AppLocalizations.of(context)!.moduleAdmin,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleAdminDescription,
+ root: AdminRouter.root,
+ );
+
AdminRouter(this.ref);
QRoute route() => QRoute(
@@ -61,90 +63,37 @@ class AdminRouter {
AdminMiddleware(ref, isAdminProvider),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
children: [
QRoute(
- path: groups,
- builder: () => group_page.GroupsPage(),
- middleware: [DeferredLoadingMiddleware(group_page.loadLibrary)],
- children: [
- QRoute(
- path: addGroup,
- builder: () => add_group_page.AddGroupPage(),
- middleware: [DeferredLoadingMiddleware(add_group_page.loadLibrary)],
- ),
- QRoute(
- path: editGroup,
- builder: () => edit_group_page.EditGroupPage(),
- middleware: [
- DeferredLoadingMiddleware(edit_group_page.loadLibrary),
- ],
- ),
- QRoute(
- path: addLoaner,
- builder: () => add_loaner_page.AddLoanerPage(),
- middleware: [
- DeferredLoadingMiddleware(add_loaner_page.loadLibrary),
- ],
- ),
- ],
- ),
- QRoute(
- path: editModuleVisibility,
- builder: () => edit_module_visibility.EditModulesVisibilityPage(),
+ path: usersManagement,
+ builder: () => users_managmement_page.UsersManagementPage(),
middleware: [
- DeferredLoadingMiddleware(edit_module_visibility.loadLibrary),
+ DeferredLoadingMiddleware(users_managmement_page.loadLibrary),
],
),
QRoute(
- path: schools,
- builder: () => school_page.SchoolsPage(),
- middleware: [DeferredLoadingMiddleware(school_page.loadLibrary)],
+ path: usersGroups,
+ builder: () => groups_page.GroupsPage(),
+ middleware: [DeferredLoadingMiddleware(groups_page.loadLibrary)],
children: [
QRoute(
- path: addSchool,
- builder: () => add_school_page.AddSchoolPage(),
- middleware: [
- DeferredLoadingMiddleware(add_school_page.loadLibrary),
- ],
- ),
- QRoute(
- path: editSchool,
- builder: () => edit_school_page.EditSchoolPage(),
+ path: editGroup,
+ builder: () => edit_group_page.EditGroupPage(),
middleware: [
- DeferredLoadingMiddleware(edit_school_page.loadLibrary),
+ DeferredLoadingMiddleware(edit_group_page.loadLibrary),
],
),
],
),
QRoute(
- path: associationMemberships,
- builder: () => association_membership_page.AssociationMembershipsPage(),
+ path: groupNotification,
+ builder: () => group_notification_page.GroupNotificationPage(),
middleware: [
- DeferredLoadingMiddleware(association_membership_page.loadLibrary),
- ],
- children: [
- QRoute(
- path: detailAssociationMembership,
- builder: () =>
- association_membership_detail_page.AssociationMembershipEditorPage(),
- middleware: [
- DeferredLoadingMiddleware(
- association_membership_detail_page.loadLibrary,
- ),
- ],
- children: [
- QRoute(
- path: addEditMember,
- builder: () =>
- add_edit_user_membership_page.AddEditUserMembershipPage(),
- middleware: [
- DeferredLoadingMiddleware(
- add_edit_user_membership_page.loadLibrary,
- ),
- ],
- ),
- ],
- ),
+ DeferredLoadingMiddleware(group_notification_page.loadLibrary),
],
),
QRoute(
@@ -192,6 +141,11 @@ class AdminRouter {
),
],
),
+ QRoute(
+ path: association,
+ builder: () => association_page.AssociationPage(),
+ middleware: [DeferredLoadingMiddleware(association_page.loadLibrary)],
+ ),
],
);
}
diff --git a/lib/admin/tools/constants.dart b/lib/admin/tools/constants.dart
deleted file mode 100644
index aa9eef2746..0000000000
--- a/lib/admin/tools/constants.dart
+++ /dev/null
@@ -1,92 +0,0 @@
-class AdminTextConstants {
- static const String accountTypes = "Types de compte";
- static const String add = "Ajouter";
- static const String addGroup = "Ajouter un groupe";
- static const String addMember = "Ajouter un membre";
- static const String addedGroup = "Groupe créé";
- static const String addedLoaner = "Préteur ajouté";
- static const String addedMember = "Membre ajouté";
- static const String addingError = "Erreur lors de l'ajout";
- static const String addingMember = "Ajout d'un membre";
- static const String addLoaningGroup = "Ajouter un groupe de prêt";
- static const String addSchool = "Ajouter une école";
- static const String addStructure = "Ajouter une structure";
- static const String addedSchool = "École créée";
- static const String addedStructure = "Structure ajoutée";
- static const String editedStructure = "Structure modifiée";
- static const String administration = "Administration";
- static const String associationMembership = "Adhésion";
- static const String associationMembershipName = "Nom de l'adhésion";
- static const String associationsMemberships = "Adhésions";
- static const String clearFilters = "Effacer les filtres";
- static const String createAssociationMembership = "Créer une adhésion";
- static const String createdAssociationMembership = "Adhésion créée";
- static const String creationError = "Erreur lors de la création";
- static const String dateError =
- "La date de début doit être avant la date de fin";
- static const String delete = "Supprimer";
- static const String deleteAssociationMembership = "Supprimer l'adhésion ?";
- static const String deletedAssociationMembership = "Adhésion supprimée";
- static const String deleteGroup = "Supprimer le groupe ?";
- static const String deletedGroup = "Groupe supprimé";
- static const String deleteSchool = "Supprimer l'école ?";
- static const String deletedSchool = "École supprimée";
- static const String deleting = "Suppression";
- static const String deletingError = "Erreur lors de la suppression";
- static const String description = "Description";
- static const String eclSchool = "Centrale Lyon";
- static const String edit = "Modifier";
- static const String editStructure = "Modifier la structure";
- static const String editMembership = "Modifier l'adhésion";
- static const String emptyDate = "Date vide";
- static const String emptyFieldError = "Le nom ne peut pas être vide";
- static const String emailRegex = "Email Regex";
- static const String emptyUser = "Utilisateur vide";
- static const String endDate = "Date de fin";
- static const String endDateMaximal = "Date de fin maximale";
- static const String endDateMinimal = "Date de fin minimale";
- static const String error = "Erreur";
- static const String filters = "Filtres";
- static const String group = "Groupe";
- static const String groups = "Groupes";
- static const String loaningGroup = "Groupe de prêt";
- static const String looking = "Recherche";
- static const String manager = "Administrateur de la structure";
- static const String maximum = "Maximum";
- static const String members = "Membres";
- static const String membershipAddingError =
- "Erreur lors de l'ajout (surement dû à une superposition de dates)";
- static const String memberships = "Adhésions";
- static String membershipUpdatingError =
- "Erreur lors de la modification (surement dû à une superposition de dates)";
- static const String minimum = "Minimum";
- static const String modifyModuleVisibility = "Visibilité des modules";
- static const String myEclPay = "MyECLPay";
- static const String name = "Nom";
- static const String noManager = "Aucun manager n'est sélectionné";
- static const String noMember = "Aucun membre";
- static const String noMoreLoaner = "Aucun prêteur n'est disponible";
- static const String noSchool = "Sans école";
- static const String removeGroupMember = "Supprimer le membre du groupe ?";
- static const String research = "Recherche";
- static const String schools = "Écoles";
- static const String structures = "Structures";
- static const String startDate = "Date de début";
- static const String startDateMaximal = "Date de début maximale";
- static const String startDateMinimal = "Date de début minimale";
- static const String updatedAssociationMembership = "Adhésion modifiée";
- static const String updatedGroup = "Groupe modifié";
- static const String updatedMembership = "Adhésion modifiée";
- static const String updatingError = "Erreur lors de la modification";
- static const String user = "Utilisateur";
- static const String validateFilters = "Valider les filtres";
- static const String visibilities = "Visibilités";
-}
-
-enum SchoolIdConstant {
- noSchool("dce19aa2-8863-4c93-861e-fb7be8f610ed"),
- eclSchool("d9772da7-1142-4002-8b86-b694b431dfed");
-
- const SchoolIdConstant(this.value);
- final String value;
-}
diff --git a/lib/admin/tools/function.dart b/lib/admin/tools/function.dart
deleted file mode 100644
index dbeff77c60..0000000000
--- a/lib/admin/tools/function.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-import 'package:titan/admin/tools/constants.dart';
-
-String getSchoolNameFromId(String id, String name) {
- if (id == SchoolIdConstant.noSchool.value) {
- return AdminTextConstants.noSchool;
- }
- if (id == SchoolIdConstant.eclSchool.value) {
- return AdminTextConstants.eclSchool;
- }
- return name;
-}
diff --git a/lib/admin/tools/functions.dart b/lib/admin/tools/functions.dart
new file mode 100644
index 0000000000..4c927c6c6d
--- /dev/null
+++ b/lib/admin/tools/functions.dart
@@ -0,0 +1,38 @@
+List parseCsvContent(String content) {
+ if (content.isEmpty) return [];
+
+ final separators = [',', ';', '\t', '|'];
+
+ // Simple heuristic: count occurrences of each separator in the entire content
+ String bestSeparator = ',';
+ int maxOccurrences = 0;
+
+ for (final separator in separators) {
+ final occurrences = separator.allMatches(content).length;
+
+ if (occurrences > maxOccurrences) {
+ maxOccurrences = occurrences;
+ bestSeparator = separator;
+ }
+ }
+
+ final lines = content.split('\n').where((line) => line.trim().isNotEmpty);
+ final result = [];
+
+ for (final line in lines) {
+ final fields = line
+ .split(bestSeparator)
+ .map((field) => field.trim())
+ .where((field) => field.isNotEmpty && _isValidEmail(field));
+ result.addAll(fields);
+ }
+
+ return result.toSet().toList();
+}
+
+bool _isValidEmail(String email) {
+ final emailRegex = RegExp(
+ r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
+ );
+ return emailRegex.hasMatch(email.trim());
+}
diff --git a/lib/admin/ui/admin.dart b/lib/admin/ui/admin.dart
deleted file mode 100644
index 4d40551181..0000000000
--- a/lib/admin/ui/admin.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/widgets/top_bar.dart';
-import 'package:titan/user/providers/user_provider.dart';
-
-class AdminTemplate extends HookConsumerWidget {
- final Widget child;
- const AdminTemplate({super.key, required this.child});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final meNotifier = ref.watch(asyncUserProvider.notifier);
- return Scaffold(
- body: Container(
- decoration: const BoxDecoration(color: Colors.white),
- child: SafeArea(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- TopBar(
- title: AdminTextConstants.administration,
- root: AdminRouter.root,
- onMenu: () {
- tokenExpireWrapper(ref, () async {
- await meNotifier.loadMe();
- });
- },
- ),
- Expanded(child: child),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/components/user_ui.dart b/lib/admin/ui/components/user_ui.dart
index d7ca60174d..681e426bb9 100644
--- a/lib/admin/ui/components/user_ui.dart
+++ b/lib/admin/ui/components/user_ui.dart
@@ -29,11 +29,7 @@ class UserUi extends HookConsumerWidget {
child: Container(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(
- gradient: const LinearGradient(
- colors: [ColorConstants.background2, Colors.black],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
+ color: ColorConstants.onMain,
boxShadow: [
BoxShadow(
color: ColorConstants.background2.withValues(alpha: 0.4),
diff --git a/lib/admin/ui/pages/add_edit_structure_page/add_edit_structure_page.dart b/lib/admin/ui/pages/add_edit_structure_page/add_edit_structure_page.dart
deleted file mode 100644
index c9fb207b80..0000000000
--- a/lib/admin/ui/pages/add_edit_structure_page/add_edit_structure_page.dart
+++ /dev/null
@@ -1,195 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/association_membership_simple.dart';
-import 'package:titan/admin/providers/association_membership_list_provider.dart';
-import 'package:titan/admin/providers/structure_manager_provider.dart';
-import 'package:titan/admin/providers/structure_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/components/admin_button.dart';
-import 'package:titan/admin/ui/components/text_editing.dart';
-import 'package:titan/admin/ui/pages/add_edit_structure_page/search_user.dart';
-import 'package:titan/paiement/class/structure.dart';
-import 'package:titan/paiement/providers/structure_list_provider.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
-import 'package:titan/tools/ui/layouts/item_chip.dart';
-import 'package:titan/tools/ui/widgets/align_left_text.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/user/class/simple_users.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class AddEditStructurePage extends HookConsumerWidget {
- const AddEditStructurePage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final key = GlobalKey();
- final structure = ref.watch(structureProvider);
- final structureManager = ref.watch(structureManagerProvider);
- final structureManagerNotifier = ref.watch(
- structureManagerProvider.notifier,
- );
- final structureListNotifier = ref.watch(structureListProvider.notifier);
- final isEdit = structure.id != '';
- final name = useTextEditingController(text: isEdit ? structure.name : null);
- final allAssociationMembershipList = ref.watch(
- allAssociationMembershipListProvider,
- );
- final currentMembership = useState(
- (isEdit)
- ? structure.associationMembership
- : AssociationMembership.empty(),
- );
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- return AdminTemplate(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: SingleChildScrollView(
- physics: const AlwaysScrollableScrollPhysics(
- parent: BouncingScrollPhysics(),
- ),
- child: Form(
- key: key,
- child: Column(
- children: [
- const SizedBox(height: 20),
- AlignLeftText(
- isEdit
- ? AdminTextConstants.editStructure
- : AdminTextConstants.addStructure,
- ),
- const SizedBox(height: 20),
- TextEditing(controller: name, label: AdminTextConstants.name),
- AsyncChild(
- value: allAssociationMembershipList,
- builder: (context, allAssociationMembershipList) {
- return HorizontalListView.builder(
- height: 40,
- items: [
- ...allAssociationMembershipList,
- AssociationMembership.empty(),
- ],
- itemBuilder: (context, associationMembership, index) {
- final selected =
- currentMembership.value.id ==
- associationMembership.id;
- return ItemChip(
- selected: selected,
- onTap: () async {
- currentMembership.value = associationMembership;
- },
- child: Text(
- associationMembership.name.toUpperCase(),
- style: TextStyle(
- color: selected ? Colors.white : Colors.black,
- fontWeight: FontWeight.bold,
- ),
- ),
- );
- },
- );
- },
- ),
- const SizedBox(height: 20),
- isEdit
- ? Column(
- children: [
- Text(
- AdminTextConstants.manager,
- style: TextStyle(
- color: ColorConstants.gradient1,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 10),
- Text(
- structureManager.getName(),
- style: TextStyle(
- color: ColorConstants.gradient1,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 10),
- ],
- )
- : const SearchUser(),
- const SizedBox(height: 20),
- WaitingButton(
- onTap: () async {
- if (key.currentState == null) {
- return;
- }
- if (structureManager.id.isEmpty && !isEdit) {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.noManager,
- );
- return;
- }
- if (key.currentState!.validate()) {
- await tokenExpireWrapper(ref, () async {
- final value = isEdit
- ? await structureListNotifier.updateStructure(
- Structure(
- name: name.text,
- associationMembership:
- currentMembership.value,
- managerUser: structureManager,
- id: structure.id,
- ),
- )
- : await structureListNotifier.createStructure(
- Structure(
- name: name.text,
- associationMembership:
- currentMembership.value,
- managerUser: structureManager,
- id: '',
- ),
- );
- if (value) {
- QR.back();
- structureManagerNotifier.setUser(SimpleUser.empty());
- displayToastWithContext(
- TypeMsg.msg,
- isEdit
- ? AdminTextConstants.editedStructure
- : AdminTextConstants.addedStructure,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.addingError,
- );
- }
- });
- }
- },
- builder: (child) => AdminButton(child: child),
- child: Text(
- isEdit ? AdminTextConstants.edit : AdminTextConstants.add,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Colors.white,
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/add_edit_structure_page/results.dart b/lib/admin/ui/pages/add_edit_structure_page/results.dart
deleted file mode 100644
index 351f91eea7..0000000000
--- a/lib/admin/ui/pages/add_edit_structure_page/results.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/structure_manager_provider.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/user/providers/user_list_provider.dart';
-
-class MemberResults extends HookConsumerWidget {
- const MemberResults({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final users = ref.watch(userList);
- final usersNotifier = ref.watch(userList.notifier);
- final structureManagerNotifier = ref.watch(
- structureManagerProvider.notifier,
- );
-
- return AsyncChild(
- value: users,
- builder: (context, value) => Column(
- children: value
- .map(
- (e) => Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- child: Text(
- e.getName(),
- style: const TextStyle(fontSize: 15),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- Row(
- children: [
- WaitingButton(
- onTap: () async {
- structureManagerNotifier.setUser(e);
- usersNotifier.clear();
- // TODO: Confirmation dialog
- },
- waitingColor: ColorConstants.gradient1,
- builder: (child) => child,
- child: const HeroIcon(HeroIcons.plus),
- ),
- ],
- ),
- ],
- ),
- ),
- )
- .toList(),
- ),
- loaderColor: ColorConstants.gradient1,
- );
- }
-}
diff --git a/lib/admin/ui/pages/add_edit_structure_page/search_user.dart b/lib/admin/ui/pages/add_edit_structure_page/search_user.dart
deleted file mode 100644
index 688a12cd00..0000000000
--- a/lib/admin/ui/pages/add_edit_structure_page/search_user.dart
+++ /dev/null
@@ -1,67 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/structure_manager_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/pages/add_edit_structure_page/results.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/widgets/styled_search_bar.dart';
-import 'package:titan/user/class/simple_users.dart';
-import 'package:titan/user/providers/user_list_provider.dart';
-
-class SearchUser extends HookConsumerWidget {
- const SearchUser({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final usersNotifier = ref.watch(userList.notifier);
- final structureManager = ref.watch(structureManagerProvider);
-
- return Column(
- children: [
- StyledSearchBar(
- label: AdminTextConstants.manager,
- color: ColorConstants.gradient1,
- padding: const EdgeInsets.all(0),
- editingController: useTextEditingController(
- text: structureManager.id == SimpleUser.empty().id
- ? ""
- : structureManager.getName(),
- ),
- onChanged: (value) async {
- if (value.isNotEmpty) {
- await usersNotifier.filterUsers(value);
- } else {
- usersNotifier.clear();
- }
- },
- suffixIcon: Padding(
- padding: const EdgeInsets.all(7.0),
- child: Container(
- padding: const EdgeInsets.all(7),
- decoration: BoxDecoration(
- gradient: const LinearGradient(
- colors: [ColorConstants.gradient1, ColorConstants.gradient2],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: ColorConstants.gradient2.withValues(alpha: 0.4),
- offset: const Offset(2, 3),
- blurRadius: 5,
- ),
- ],
- borderRadius: const BorderRadius.all(Radius.circular(10)),
- ),
- child: HeroIcon(HeroIcons.plus, size: 20, color: Colors.white),
- ),
- ),
- ),
- const SizedBox(height: 10),
- const MemberResults(),
- ],
- );
- }
-}
diff --git a/lib/admin/ui/pages/association_page/add_association_modal.dart b/lib/admin/ui/pages/association_page/add_association_modal.dart
new file mode 100644
index 0000000000..1ac2b309c8
--- /dev/null
+++ b/lib/admin/ui/pages/association_page/add_association_modal.dart
@@ -0,0 +1,142 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:titan/admin/class/simple_group.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/styleguide/list_item_template.dart';
+import 'package:titan/tools/ui/widgets/text_entry.dart';
+
+class AddAssociationModal extends HookWidget {
+ final List groups;
+ final void Function(SimpleGroup group, String name) onSubmit;
+ final WidgetRef ref;
+
+ const AddAssociationModal({
+ super.key,
+ required this.groups,
+ required this.onSubmit,
+ required this.ref,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final nameController = useTextEditingController();
+ final chosenGroup = useState(null);
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return BottomModalTemplate(
+ title: localizeWithContext.adminAddAssociation,
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: TextEntry(
+ label: localizeWithContext.adminAssociationName,
+ controller: nameController,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: chosenGroup.value == null
+ ? ListItem(
+ title: localizeWithContext
+ .adminChooseAssociationManagerGroup,
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminChooseGroup,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 600),
+ child: SingleChildScrollView(
+ physics: BouncingScrollPhysics(),
+ child: Column(
+ children: [
+ ...groups.map(
+ (e) => ListItemTemplate(
+ title: e.name,
+ trailing: const HeroIcon(
+ HeroIcons.plus,
+ ),
+ onTap: () {
+ chosenGroup.value = e;
+ Navigator.of(ctx).pop();
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ )
+ : ListItem(
+ title: localizeWithContext.adminManagerGroup(
+ chosenGroup.value!.name,
+ ),
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminChooseGroup,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 600),
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ ...groups.map(
+ (e) => ListItemTemplate(
+ title: e.name,
+ trailing: const HeroIcon(
+ HeroIcons.plus,
+ ),
+ onTap: () {
+ chosenGroup.value = e;
+ Navigator.of(ctx).pop();
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ const SizedBox(height: 10),
+ Button(
+ text: localizeWithContext.adminAdd,
+ onPressed: () {
+ if (chosenGroup.value != null) {
+ onSubmit(chosenGroup.value!, nameController.text);
+ }
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/association_page/association_item.dart b/lib/admin/ui/pages/association_page/association_item.dart
new file mode 100644
index 0000000000..08b0a8267c
--- /dev/null
+++ b/lib/admin/ui/pages/association_page/association_item.dart
@@ -0,0 +1,65 @@
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/class/assocation.dart';
+import 'package:titan/admin/providers/all_groups_list_provider.dart';
+import 'package:titan/admin/providers/association_logo_provider.dart';
+import 'package:titan/admin/providers/associations_logo_map_provider.dart';
+import 'package:titan/admin/ui/pages/association_page/edit_association.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/builders/auto_loader_child.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+
+class AssociationItem extends HookConsumerWidget {
+ const AssociationItem({super.key, required this.association});
+
+ final Association association;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final groups = ref.watch(allGroupList);
+ final group = groups.firstWhere((group) => group.id == association.groupId);
+ final associationLogo = ref.watch(
+ associationLogoMapProvider.select((value) => value[association.id]),
+ );
+ final associationLogoMapNotifier = ref.watch(
+ associationLogoMapProvider.notifier,
+ );
+ final associationLogoNotifier = ref.watch(associationLogoProvider.notifier);
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 5),
+ child: ListItem(
+ title: association.name,
+ subtitle: localizeWithContext.adminManagerGroup(group.name),
+ icon: AutoLoaderChild(
+ group: associationLogo,
+ notifier: associationLogoMapNotifier,
+ mapKey: association.id,
+ loader: (associationId) =>
+ associationLogoNotifier.getAssociationLogo(associationId),
+ dataBuilder: (context, data) {
+ return CircleAvatar(
+ radius: 20,
+ backgroundColor: Colors.white,
+ backgroundImage: Image(image: data.first.image).image,
+ );
+ },
+ ),
+ onTap: () async {
+ associationLogoNotifier.getAssociationLogo(association.id);
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminEditAssociation(association.name),
+ child: EditAssociation(association: association, group: group),
+ ),
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/association_page/association_page.dart b/lib/admin/ui/pages/association_page/association_page.dart
new file mode 100644
index 0000000000..01b83c84a7
--- /dev/null
+++ b/lib/admin/ui/pages/association_page/association_page.dart
@@ -0,0 +1,122 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/class/assocation.dart';
+import 'package:titan/admin/providers/all_groups_list_provider.dart';
+import 'package:titan/admin/providers/assocation_list_provider.dart';
+import 'package:titan/admin/ui/pages/association_page/add_association_modal.dart';
+import 'package:titan/admin/ui/pages/association_page/association_item.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+
+class AssociationPage extends ConsumerWidget {
+ const AssociationPage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final associationList = ref.watch(associationListProvider);
+ final associationNotifier = ref.watch(associationListProvider.notifier);
+ final groups = ref.watch(allGroupList);
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ void popWithContext() {
+ Navigator.of(context).pop();
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return AdminTemplate(
+ child: Refresher(
+ controller: ScrollController(),
+ onRefresh: () async {
+ await associationNotifier.loadAssociations();
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Column(
+ children: [
+ const SizedBox(height: 20),
+ Row(
+ children: [
+ Text(
+ localizeWithContext.adminAssociations,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
+ ),
+ ),
+ const Spacer(),
+ CustomIconButton(
+ icon: HeroIcon(
+ HeroIcons.plus,
+ color: Colors.white,
+ size: 30,
+ ),
+ onPressed: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: AddAssociationModal(
+ groups: groups,
+ onSubmit: (group, name) {
+ tokenExpireWrapper(ref, () async {
+ final value = await associationNotifier
+ .createAssociation(
+ Association.empty().copyWith(
+ groupId: group.id,
+ name: name,
+ ),
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext.adminAssociationCreated,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext
+ .adminAssociationCreationError,
+ );
+ }
+ popWithContext();
+ });
+ },
+ ref: ref,
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ SizedBox(height: 10),
+ AsyncChild(
+ value: associationList,
+ builder: (BuildContext context, associationList) {
+ return Column(
+ children: [
+ ...associationList.map(
+ (association) =>
+ AssociationItem(association: association),
+ ),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/association_page/edit_association.dart b/lib/admin/ui/pages/association_page/edit_association.dart
new file mode 100644
index 0000000000..228730ccc1
--- /dev/null
+++ b/lib/admin/ui/pages/association_page/edit_association.dart
@@ -0,0 +1,226 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:image_picker/image_picker.dart';
+import 'package:titan/admin/class/assocation.dart';
+import 'package:titan/admin/class/simple_group.dart';
+import 'package:titan/admin/providers/all_groups_list_provider.dart';
+import 'package:titan/admin/providers/assocation_list_provider.dart';
+import 'package:titan/admin/providers/association_logo_provider.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/settings/ui/pages/main_page/picture_button.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/styleguide/text_entry.dart';
+
+class EditAssociation extends HookConsumerWidget {
+ final Association association;
+ final SimpleGroup group;
+ const EditAssociation({
+ super.key,
+ required this.association,
+ required this.group,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final associationListNotifier = ref.watch(associationListProvider.notifier);
+ final nameController = useTextEditingController(text: association.name);
+ final groups = ref.watch(allGroupList);
+ final chosenGroup = useState(group);
+ final associationLogo = ref.watch(associationLogoProvider);
+ final associationLogoNotifier = ref.watch(associationLogoProvider.notifier);
+
+ MediaQuery.of(context).viewInsets.bottom;
+
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+ final navigatorWithContext = Navigator.of(context);
+
+ return SingleChildScrollView(
+ child: Column(
+ children: [
+ if (View.of(context).viewInsets.bottom == 0)
+ AsyncChild(
+ value: associationLogo,
+ builder: (context, associationLogo) {
+ return Center(
+ child: Stack(
+ clipBehavior: Clip.none,
+ children: [
+ Container(
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withValues(alpha: 0.1),
+ spreadRadius: 5,
+ blurRadius: 10,
+ offset: const Offset(2, 3),
+ ),
+ ],
+ ),
+ child: CircleAvatar(
+ radius: 80,
+ backgroundImage: Image(
+ image: associationLogo.image,
+ ).image,
+ ),
+ ),
+ Positioned(
+ bottom: 0,
+ left: 0,
+ child: GestureDetector(
+ onTap: () async {
+ final value = await associationLogoNotifier.setLogo(
+ ImageSource.gallery,
+ association.id,
+ );
+ if (value != null) {
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext
+ .adminUpdatedAssociationLogo,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext.adminTooHeavyLogo,
+ );
+ }
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext
+ .adminFailedToUpdateAssociationLogo,
+ );
+ }
+ },
+ child: const PictureButton(icon: HeroIcons.photo),
+ ),
+ ),
+ Positioned(
+ bottom: 0,
+ right: 0,
+ child: GestureDetector(
+ onTap: () async {
+ final value = await associationLogoNotifier.setLogo(
+ ImageSource.camera,
+ association.id,
+ );
+ if (value != null) {
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext
+ .adminUpdatedAssociationLogo,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext.adminTooHeavyLogo,
+ );
+ }
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext
+ .adminFailedToUpdateAssociationLogo,
+ );
+ }
+ },
+ child: const PictureButton(icon: HeroIcons.camera),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ SizedBox(height: View.of(context).viewInsets.bottom == 0 ? 30 : 10),
+ TextEntry(
+ label: localizeWithContext.adminAssociationName,
+ controller: nameController,
+ ),
+ SizedBox(height: 20),
+ ListItem(
+ title: localizeWithContext.adminManagerGroup(
+ chosenGroup.value!.name,
+ ),
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminChooseGroup,
+ child: ConstrainedBox(
+ constraints: BoxConstraints(maxHeight: 600),
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ ...groups.map(
+ (e) => ListItem(
+ title: e.name,
+ onTap: () {
+ chosenGroup.value = e;
+ Navigator.of(ctx).pop();
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ SizedBox(height: 20),
+ Button(
+ text: localizeWithContext.globalConfirm,
+ disabled:
+ !(nameController.value.text != association.name ||
+ chosenGroup.value!.id != association.groupId),
+ onPressed: () async {
+ await tokenExpireWrapper(ref, () async {
+ final newAssociation = association.copyWith(
+ name: nameController.value.text,
+ groupId: chosenGroup.value!.id,
+ );
+ final value = await associationListNotifier.updateAssociation(
+ newAssociation,
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext.adminAssociationUpdated,
+ );
+ navigatorWithContext.pop();
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext.adminAssociationUpdateError,
+ );
+ }
+ });
+ },
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/group_notifification_page/group_notification_page.dart b/lib/admin/ui/pages/group_notifification_page/group_notification_page.dart
new file mode 100644
index 0000000000..d1fb25598f
--- /dev/null
+++ b/lib/admin/ui/pages/group_notifification_page/group_notification_page.dart
@@ -0,0 +1,155 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/providers/group_list_provider.dart';
+import 'package:titan/admin/repositories/notification_repository.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/styleguide/text_entry.dart';
+
+class GroupNotificationPage extends HookConsumerWidget {
+ const GroupNotificationPage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final groups = ref.watch(allGroupListProvider);
+ final groupsNotifier = ref.watch(allGroupListProvider.notifier);
+ final notificationRepository = ref.watch(notificationRepositoryProvider);
+ final titleController = useTextEditingController();
+ final contentController = useTextEditingController();
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+ return AdminTemplate(
+ child: Refresher(
+ controller: ScrollController(),
+ onRefresh: () async {
+ await groupsNotifier.loadGroups();
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
+ child: Column(
+ children: [
+ const SizedBox(height: 20),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ localizeWithContext.adminGroupNotification,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
+ ),
+ ),
+ ),
+ const SizedBox(height: 10),
+ AsyncChild(
+ value: groups,
+ builder: (context, g) {
+ g.sort(
+ (a, b) =>
+ a.name.toLowerCase().compareTo(b.name.toLowerCase()),
+ );
+ return Column(
+ children: [
+ Column(
+ children: [
+ ...g.map(
+ (group) => Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 5.0,
+ ),
+ child: ListItem(
+ title: group.name,
+ subtitle: group.description,
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext
+ .adminNotifyGroup(group.name),
+ child: Column(
+ children: [
+ TextEntry(
+ label:
+ localizeWithContext.adminTitle,
+ controller: titleController,
+ keyboardType:
+ TextInputType.multiline,
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ label: localizeWithContext
+ .adminContent,
+ controller: contentController,
+ keyboardType:
+ TextInputType.multiline,
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext.adminSend,
+ onPressed: () {
+ notificationRepository
+ .sendNotification(
+ group.id,
+ titleController.text,
+ contentController.text,
+ )
+ .then((value) {
+ if (value) {
+ QR.back();
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext
+ .adminNotificationSent,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext
+ .adminFailedToSendNotification,
+ );
+ }
+ })
+ .catchError((error) {
+ displayToastWithContext(
+ TypeMsg.error,
+ error.toString(),
+ );
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ],
+ );
+ },
+ loaderColor: ColorConstants.main,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/groups/add_group_page/add_group_page.dart b/lib/admin/ui/pages/groups/add_group_page/add_group_page.dart
deleted file mode 100644
index a42957311a..0000000000
--- a/lib/admin/ui/pages/groups/add_group_page/add_group_page.dart
+++ /dev/null
@@ -1,88 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/simple_group.dart';
-import 'package:titan/admin/providers/group_list_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/components/admin_button.dart';
-import 'package:titan/admin/ui/components/text_editing.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/widgets/align_left_text.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class AddGroupPage extends HookConsumerWidget {
- const AddGroupPage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final key = GlobalKey();
- final name = useTextEditingController();
- final description = useTextEditingController();
- final groupListNotifier = ref.watch(allGroupListProvider.notifier);
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- return AdminTemplate(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: SingleChildScrollView(
- physics: const AlwaysScrollableScrollPhysics(
- parent: BouncingScrollPhysics(),
- ),
- child: Form(
- key: key,
- child: Column(
- children: [
- const AlignLeftText(AdminTextConstants.addGroup),
- const SizedBox(height: 30),
- TextEditing(controller: name, label: AdminTextConstants.name),
- TextEditing(
- controller: description,
- label: AdminTextConstants.description,
- ),
- WaitingButton(
- onTap: () async {
- await tokenExpireWrapper(ref, () async {
- final value = await groupListNotifier.createGroup(
- SimpleGroup(
- name: name.text,
- description: description.text,
- id: '',
- ),
- );
- if (value) {
- QR.back();
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.addedGroup,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.addingError,
- );
- }
- });
- },
- builder: (child) => AdminButton(child: child),
- child: const Text(
- AdminTextConstants.add,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Colors.white,
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/groups/add_loaner_page/add_loaner_page.dart b/lib/admin/ui/pages/groups/add_loaner_page/add_loaner_page.dart
deleted file mode 100644
index 058a9e6216..0000000000
--- a/lib/admin/ui/pages/groups/add_loaner_page/add_loaner_page.dart
+++ /dev/null
@@ -1,119 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/group_list_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/loan/class/loaner.dart';
-import 'package:titan/loan/providers/all_loaner_list_provider.dart';
-import 'package:titan/loan/providers/loaner_list_provider.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/widgets/align_left_text.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class AddLoanerPage extends HookConsumerWidget {
- const AddLoanerPage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final loanerListNotifier = ref.watch(loanerListProvider.notifier);
- final loaners = ref.watch(allLoanerList);
- final associations = ref.watch(allGroupListProvider);
- final loanersId = loaners.map((x) => x.groupManagerId).toList();
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- return AdminTemplate(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: SingleChildScrollView(
- physics: const AlwaysScrollableScrollPhysics(
- parent: BouncingScrollPhysics(),
- ),
- child: Column(
- children: [
- SizedBox(
- child: Column(
- children: [
- const AlignLeftText(AdminTextConstants.addLoaningGroup),
- const SizedBox(height: 30),
- AsyncChild(
- value: associations,
- builder: (context, associationList) {
- final canAdd = associationList
- .where((x) => !loanersId.contains(x.id))
- .toList();
- return canAdd.isNotEmpty
- ? Column(
- children: canAdd
- .map(
- (e) => GestureDetector(
- onTap: () {
- Loaner newLoaner = Loaner(
- groupManagerId: e.id,
- id: '',
- name: e.name,
- );
- tokenExpireWrapper(ref, () async {
- final value =
- await loanerListNotifier
- .addLoaner(newLoaner);
- if (value) {
- QR.back();
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.addedLoaner,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.addingError,
- );
- }
- });
- },
- child: Container(
- padding: const EdgeInsets.symmetric(
- vertical: 20,
- ),
- child: Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceBetween,
- children: [
- Text(
- e.name,
- style: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w500,
- ),
- ),
- const HeroIcon(
- HeroIcons.plus,
- size: 25,
- color: Colors.black,
- ),
- ],
- ),
- ),
- ),
- )
- .toList(),
- )
- : const Center(
- child: Text(AdminTextConstants.noMoreLoaner),
- );
- },
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/groups/edit_group_page/edit_group_page.dart b/lib/admin/ui/pages/groups/edit_group_page/edit_group_page.dart
index a906c41534..76b16c7fc9 100644
--- a/lib/admin/ui/pages/groups/edit_group_page/edit_group_page.dart
+++ b/lib/admin/ui/pages/groups/edit_group_page/edit_group_page.dart
@@ -1,145 +1,45 @@
import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/group.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/providers/group_from_simple_group_provider.dart';
import 'package:titan/admin/providers/group_id_provider.dart';
-import 'package:titan/admin/providers/group_list_provider.dart';
import 'package:titan/admin/providers/group_provider.dart';
-import 'package:titan/admin/providers/simple_groups_groups_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/components/admin_button.dart';
import 'package:titan/admin/ui/pages/groups/edit_group_page/search_user.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/builders/auto_loader_child.dart';
-import 'package:titan/tools/ui/widgets/align_left_text.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/tools/ui/widgets/text_entry.dart';
-import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/tools/ui/builders/single_auto_loader_child.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
-class EditGroupPage extends HookConsumerWidget {
+class EditGroupPage extends ConsumerWidget {
const EditGroupPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final groupId = ref.watch(groupIdProvider);
- final groupNotifier = ref.watch(groupProvider.notifier);
- final groupListNotifier = ref.watch(allGroupListProvider.notifier);
- final key = GlobalKey();
- final name = useTextEditingController();
- final description = useTextEditingController();
- final simpleGroupsGroupsNotifier = ref.watch(
- simpleGroupsGroupsProvider.notifier,
+ final group = ref.watch(
+ groupFromSimpleGroupProvider.select((map) => map[groupId]),
);
- final simpleGroupsGroups = ref.watch(
- simpleGroupsGroupsProvider.select((value) => value[groupId]),
+ final groupNotifier = ref.watch(groupProvider.notifier);
+ final groupFromSimpleGroupNotifier = ref.watch(
+ groupFromSimpleGroupProvider.notifier,
);
-
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
return AdminTemplate(
- child: SingleChildScrollView(
- physics: const BouncingScrollPhysics(),
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: AutoLoaderChild(
- group: simpleGroupsGroups,
- notifier: simpleGroupsGroupsNotifier,
- mapKey: groupId,
- loader: (groupId) async => (await groupNotifier.loadGroup(
- groupId,
- )).maybeWhen(data: (groups) => groups, orElse: () => Group.empty()),
- dataBuilder: (context, groups) {
- final group = groups.first;
- name.text = group.name;
- description.text = group.description;
- return Column(
- children: [
- const AlignLeftText(
- AdminTextConstants.edit,
- fontSize: 20,
- color: ColorConstants.gradient1,
- ),
- const SizedBox(height: 20),
- Form(
- key: key,
- child: Column(
- children: [
- Container(
- margin: const EdgeInsets.symmetric(vertical: 10),
- alignment: Alignment.centerLeft,
- child: TextEntry(
- controller: name,
- color: ColorConstants.gradient1,
- label: AdminTextConstants.name,
- suffixIcon: const HeroIcon(HeroIcons.pencil),
- enabledColor: Colors.transparent,
- ),
- ),
- Container(
- margin: const EdgeInsets.symmetric(vertical: 10),
- alignment: Alignment.centerLeft,
- child: TextEntry(
- controller: description,
- color: ColorConstants.gradient1,
- label: AdminTextConstants.description,
- suffixIcon: const HeroIcon(HeroIcons.pencil),
- enabledColor: Colors.transparent,
- ),
- ),
- const SizedBox(height: 20),
- WaitingButton(
- onTap: () async {
- if (!key.currentState!.validate()) {
- return;
- }
- await tokenExpireWrapper(ref, () async {
- Group newGroup = group.copyWith(
- name: name.text,
- description: description.text,
- );
- groupNotifier.setGroup(newGroup);
- final value = await groupListNotifier.updateGroup(
- newGroup.toSimpleGroup(),
- );
- if (value) {
- QR.back();
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.updatedGroup,
- );
- } else {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.updatingError,
- );
- }
- });
- },
- builder: (child) => AdminButton(child: child),
- child: const Text(
- AdminTextConstants.edit,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Colors.white,
- ),
- ),
- ),
- const SizedBox(height: 20),
- const SearchUser(),
- ],
- ),
- ),
- ],
- );
- },
- loaderColor: Colors.white,
+ child: Refresher(
+ controller: ScrollController(),
+ onRefresh: () async {
+ await groupNotifier.loadGroup(groupId);
+ },
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
+ child: SingleAutoLoaderChild(
+ item: group,
+ notifier: groupFromSimpleGroupNotifier,
+ mapKey: groupId,
+ loader: groupNotifier.loadGroup,
+ dataBuilder: (BuildContext context, value) {
+ return SearchUser();
+ },
+ ),
),
),
),
diff --git a/lib/admin/ui/pages/groups/edit_group_page/results.dart b/lib/admin/ui/pages/groups/edit_group_page/results.dart
index 9c3709453c..ea73f29adf 100644
--- a/lib/admin/ui/pages/groups/edit_group_page/results.dart
+++ b/lib/admin/ui/pages/groups/edit_group_page/results.dart
@@ -4,13 +4,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/admin/class/group.dart';
import 'package:titan/admin/providers/group_provider.dart';
import 'package:titan/admin/providers/simple_groups_groups_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
import 'package:titan/tools/constants.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/tools/ui/styleguide/list_item_template.dart';
import 'package:titan/user/providers/user_list_provider.dart';
+import 'package:titan/l10n/app_localizations.dart';
class MemberResults extends HookConsumerWidget {
const MemberResults({super.key});
@@ -35,60 +35,48 @@ class MemberResults extends HookConsumerWidget {
.map(
(e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- child: Text(
- e.getName(),
- style: const TextStyle(fontSize: 15),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- Row(
- children: [
- WaitingButton(
- onTap: () async {
- if (!group.value!.members.contains(e)) {
- Group newGroup = group.value!.copyWith(
- members: group.value!.members + [e],
- );
- await tokenExpireWrapper(ref, () async {
- groupNotifier.addMember(newGroup, e).then((
- value,
- ) {
- if (value) {
- simpleGroupGroupsNotifier.setTData(
- newGroup.id,
- AsyncData([newGroup]),
- );
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.addedMember,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.addingError,
- );
- }
- });
- });
- }
- },
- waitingColor: ColorConstants.gradient1,
- builder: (child) => child,
- child: const HeroIcon(HeroIcons.plus),
- ),
- ],
- ),
- ],
+ child: ListItemTemplate(
+ title: e.getName(),
+ onTap: () async {
+ if (!group.value!.members.contains(e)) {
+ Group newGroup = group.value!.copyWith(
+ members: group.value!.members + [e],
+ );
+ final addedMemberMsg = AppLocalizations.of(
+ context,
+ )!.adminAddedMember;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.adminAddingError;
+ await tokenExpireWrapper(ref, () async {
+ groupNotifier.addMember(newGroup, e).then((result) {
+ if (result) {
+ simpleGroupGroupsNotifier.setTData(
+ newGroup.id,
+ AsyncData([newGroup]),
+ );
+ value.remove(e);
+ displayToastWithContext(
+ TypeMsg.msg,
+ addedMemberMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ addingErrorMsg,
+ );
+ }
+ });
+ });
+ }
+ },
+ trailing: const HeroIcon(HeroIcons.plus),
),
),
)
.toList(),
),
- loaderColor: ColorConstants.gradient1,
+ loaderColor: ColorConstants.main,
);
}
}
diff --git a/lib/admin/ui/pages/groups/edit_group_page/search_user.dart b/lib/admin/ui/pages/groups/edit_group_page/search_user.dart
index bc5f1723f4..8fcc0629a0 100644
--- a/lib/admin/ui/pages/groups/edit_group_page/search_user.dart
+++ b/lib/admin/ui/pages/groups/edit_group_page/search_user.dart
@@ -1,150 +1,153 @@
import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/admin/class/group.dart';
-import 'package:titan/admin/providers/group_id_provider.dart';
+import 'package:titan/admin/providers/group_from_simple_group_provider.dart';
import 'package:titan/admin/providers/group_provider.dart';
-import 'package:titan/admin/providers/simple_groups_groups_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/pages/groups/edit_group_page/results.dart';
import 'package:titan/admin/ui/components/user_ui.dart';
-import 'package:titan/tools/constants.dart';
+import 'package:titan/admin/ui/pages/groups/edit_group_page/results.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+import 'package:titan/tools/ui/styleguide/searchbar.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/widgets/loader.dart';
-import 'package:titan/tools/ui/widgets/styled_search_bar.dart';
import 'package:titan/user/providers/user_list_provider.dart';
+import 'package:titan/l10n/app_localizations.dart';
class SearchUser extends HookConsumerWidget {
const SearchUser({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
- final group = ref.watch(groupProvider);
final usersNotifier = ref.watch(userList.notifier);
- final groupId = ref.watch(groupIdProvider);
final groupNotifier = ref.watch(groupProvider.notifier);
- final simpleGroupsGroups = ref.watch(simpleGroupsGroupsProvider);
- final simpleGroupGroupsNotifier = ref.watch(
- simpleGroupsGroupsProvider.notifier,
+ final group = ref.watch(groupProvider);
+ final groupFromSimpleGroupNotifier = ref.watch(
+ groupFromSimpleGroupProvider.notifier,
);
- final add = useState(false);
void displayToastWithContext(TypeMsg type, String msg) {
displayToast(context, type, msg);
}
- final simpleGroup = simpleGroupsGroups[groupId];
- if (simpleGroup == null) {
- return const Loader();
- }
+ final localizeWithContext = AppLocalizations.of(context)!;
+
return AsyncChild(
- value: simpleGroup,
+ value: group,
builder: (context, g) {
return Column(
children: [
- StyledSearchBar(
- label: AdminTextConstants.members,
- color: ColorConstants.gradient1,
- padding: const EdgeInsets.all(0),
- onChanged: (value) async {
- if (value.isNotEmpty) {
- await usersNotifier.filterUsers(
- value,
- excludeGroup: [group.value!.toSimpleGroup()],
- );
- } else {
- usersNotifier.clear();
- }
- },
- onSuffixIconTap: (focusNode, editingController) {
- add.value = !add.value;
- if (!add.value) {
- editingController.clear();
- usersNotifier.clear();
- focusNode.unfocus();
- } else {
- focusNode.requestFocus();
- }
- },
- suffixIcon: Padding(
- padding: const EdgeInsets.all(7.0),
- child: Container(
- padding: const EdgeInsets.all(7),
- decoration: BoxDecoration(
- gradient: const LinearGradient(
- colors: [
- ColorConstants.gradient1,
- ColorConstants.gradient2,
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: ColorConstants.gradient2.withValues(alpha: 0.4),
- offset: const Offset(2, 3),
- blurRadius: 5,
- ),
- ],
- borderRadius: const BorderRadius.all(Radius.circular(10)),
- ),
- child: HeroIcon(
- !add.value ? HeroIcons.plus : HeroIcons.xMark,
- size: 20,
- color: Colors.white,
+ const SizedBox(height: 20),
+ Row(
+ children: [
+ Expanded(
+ child: CustomSearchBar(
+ onSearch: (value) async {
+ if (value.isNotEmpty) {
+ await usersNotifier.filterUsers(
+ value,
+ includeGroup: [g.toSimpleGroup()],
+ );
+ } else {
+ usersNotifier.clear();
+ }
+ },
),
),
- ),
- ),
- if (add.value) const SizedBox(height: 10),
- if (add.value) const MemberResults(),
- if (!add.value)
- ...g[0].members.map(
- (x) => UserUi(
- user: x,
- onDelete: () {
- showDialog(
+ const SizedBox(width: 10),
+ CustomIconButton(
+ icon: HeroIcon(HeroIcons.plus, size: 30, color: Colors.white),
+ onPressed: () async {
+ await showCustomBottomModal(
context: context,
- builder: (BuildContext context) => CustomDialogBox(
- descriptions: AdminTextConstants.removeGroupMember,
- title: AdminTextConstants.deleting,
- onYes: () async {
- await tokenExpireWrapper(ref, () async {
- Group newGroup = g[0].copyWith(
- members: g[0].members
- .where((element) => element.id != x.id)
- .toList(),
- );
- final value = await groupNotifier.deleteMember(
- newGroup,
- x,
- );
- if (value) {
- simpleGroupGroupsNotifier.setTData(
- newGroup.id,
- AsyncData([newGroup]),
- );
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.updatedGroup,
- );
- } else {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.updatingError,
- );
- }
- });
- },
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminAddMember,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 350),
+ child: Column(
+ children: [
+ CustomSearchBar(
+ onSearch: (value) async {
+ if (value.isNotEmpty) {
+ await usersNotifier.filterUsers(
+ value,
+ excludeGroup: [g.toSimpleGroup()],
+ );
+ } else {
+ usersNotifier.clear();
+ }
+ },
+ ),
+ const SizedBox(height: 10),
+ Expanded(
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: const MemberResults(),
+ ),
+ ),
+ ],
+ ),
+ ),
),
);
},
),
+ ],
+ ),
+ const SizedBox(height: 10),
+ ...g.members.map(
+ (x) => UserUi(
+ user: x,
+ onDelete: () {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => CustomDialogBox(
+ descriptions: AppLocalizations.of(
+ context,
+ )!.adminRemoveGroupMember,
+ title: AppLocalizations.of(context)!.adminDeleting,
+ onYes: () async {
+ final updatedGroupMsg = AppLocalizations.of(
+ context,
+ )!.adminUpdatedGroup;
+ final updatingErrorMsg = AppLocalizations.of(
+ context,
+ )!.adminUpdatingError;
+ await tokenExpireWrapper(ref, () async {
+ Group newGroup = g.copyWith(
+ members: g.members
+ .where((element) => element.id != x.id)
+ .toList(),
+ );
+ final value = await groupNotifier.deleteMember(
+ newGroup,
+ x,
+ );
+ if (value) {
+ groupFromSimpleGroupNotifier.setTData(
+ newGroup.id,
+ AsyncData(newGroup),
+ );
+ displayToastWithContext(
+ TypeMsg.msg,
+ updatedGroupMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.msg,
+ updatingErrorMsg,
+ );
+ }
+ });
+ },
+ ),
+ );
+ },
),
+ ),
],
);
},
diff --git a/lib/admin/ui/pages/groups/group_page/group_button.dart b/lib/admin/ui/pages/groups/group_page/group_button.dart
deleted file mode 100644
index 9c29cae36c..0000000000
--- a/lib/admin/ui/pages/groups/group_page/group_button.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:flutter/material.dart';
-
-class GroupButton extends StatelessWidget {
- final Widget child;
- final Color gradient1;
- final Color gradient2;
- const GroupButton({
- super.key,
- required this.child,
- required this.gradient1,
- required this.gradient2,
- });
-
- @override
- Widget build(BuildContext context) {
- return Container(
- padding: const EdgeInsets.all(10),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [gradient1, gradient2],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: gradient2.withValues(alpha: 0.3),
- spreadRadius: 2,
- blurRadius: 4,
- offset: const Offset(2, 3),
- ),
- ],
- borderRadius: BorderRadius.circular(10),
- ),
- child: Center(child: child),
- );
- }
-}
diff --git a/lib/admin/ui/pages/groups/group_page/group_page.dart b/lib/admin/ui/pages/groups/group_page/group_page.dart
deleted file mode 100644
index d563677205..0000000000
--- a/lib/admin/ui/pages/groups/group_page/group_page.dart
+++ /dev/null
@@ -1,186 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/group_id_provider.dart';
-import 'package:titan/admin/providers/group_list_provider.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/components/item_card_ui.dart';
-import 'package:titan/admin/ui/pages/groups/group_page/group_ui.dart';
-import 'package:titan/loan/providers/loaner_list_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/ui/layouts/refresher.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/user/providers/user_list_provider.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class GroupsPage extends HookConsumerWidget {
- const GroupsPage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final groups = ref.watch(allGroupListProvider);
- final groupsNotifier = ref.watch(allGroupListProvider.notifier);
- final groupIdNotifier = ref.watch(groupIdProvider.notifier);
- final loans = ref.watch(loanerListProvider);
- final loanListNotifier = ref.watch(loanerListProvider.notifier);
- ref.watch(userList);
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- final List loanersId = loans.maybeWhen(
- data: (value) => value.map((e) => e.groupManagerId).toList(),
- orElse: () => [],
- );
-
- return AdminTemplate(
- child: Refresher(
- onRefresh: () async {
- await groupsNotifier.loadGroups();
- await loanListNotifier.loadLoanerList();
- },
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: Column(
- children: [
- const SizedBox(height: 20),
- const Align(
- alignment: Alignment.centerLeft,
- child: Text(
- AdminTextConstants.groups,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
- ),
- ),
- ),
- const SizedBox(height: 30),
- AsyncChild(
- value: groups,
- builder: (context, g) {
- g.sort(
- (a, b) =>
- a.name.toLowerCase().compareTo(b.name.toLowerCase()),
- );
- return Column(
- children: [
- Column(
- children: [
- GestureDetector(
- onTap: () {
- QR.to(
- AdminRouter.root +
- AdminRouter.groups +
- AdminRouter.addGroup,
- );
- },
- child: ItemCardUi(
- children: [
- const Spacer(),
- HeroIcon(
- HeroIcons.plus,
- color: Colors.grey.shade700,
- size: 40,
- ),
- const Spacer(),
- ],
- ),
- ),
- GestureDetector(
- onTap: () {
- QR.to(
- AdminRouter.root +
- AdminRouter.groups +
- AdminRouter.addLoaner,
- );
- },
- child: ItemCardUi(
- children: [
- const Spacer(),
- Stack(
- clipBehavior: Clip.none,
- children: [
- HeroIcon(
- HeroIcons.buildingLibrary,
- color: Colors.grey.shade700,
- size: 40,
- ),
- Positioned(
- right: -2,
- top: -2,
- child: HeroIcon(
- HeroIcons.plus,
- size: 15,
- color: Colors.grey.shade700,
- ),
- ),
- ],
- ),
- const Spacer(),
- ],
- ),
- ),
- ...g.map(
- (group) => GroupUi(
- group: group,
- isLoaner: loanersId.contains(group.id),
- onEdit: () {
- groupIdNotifier.setId(group.id);
- QR.to(
- AdminRouter.root +
- AdminRouter.groups +
- AdminRouter.editGroup,
- );
- },
- onDelete: () async {
- await showDialog(
- context: context,
- builder: (context) {
- return CustomDialogBox(
- title: AdminTextConstants.deleting,
- descriptions:
- AdminTextConstants.deleteGroup,
- onYes: () async {
- tokenExpireWrapper(ref, () async {
- final value = await groupsNotifier
- .deleteGroup(group);
- if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.deletedGroup,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.deletingError,
- );
- }
- });
- },
- );
- },
- );
- },
- ),
- ),
- const SizedBox(height: 20),
- ],
- ),
- ],
- );
- },
- loaderColor: ColorConstants.gradient1,
- ),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/groups/group_page/group_ui.dart b/lib/admin/ui/pages/groups/group_page/group_ui.dart
deleted file mode 100644
index faad00dce6..0000000000
--- a/lib/admin/ui/pages/groups/group_page/group_ui.dart
+++ /dev/null
@@ -1,71 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/simple_group.dart';
-import 'package:titan/admin/ui/components/item_card_ui.dart';
-import 'package:titan/admin/ui/pages/groups/group_page/group_button.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-
-class GroupUi extends HookConsumerWidget {
- final SimpleGroup group;
- final void Function() onEdit;
- final Future Function() onDelete;
- final bool isLoaner;
- const GroupUi({
- super.key,
- required this.group,
- required this.onEdit,
- required this.onDelete,
- required this.isLoaner,
- });
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- return ItemCardUi(
- children: [
- const SizedBox(width: 10),
- if (isLoaner)
- Row(
- children: [
- HeroIcon(HeroIcons.buildingLibrary, color: Colors.grey.shade700),
- const SizedBox(width: 15),
- ],
- ),
- Expanded(
- child: Text(
- group.name,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- const SizedBox(width: 10),
- Row(
- children: [
- GestureDetector(
- onTap: onEdit,
- child: GroupButton(
- gradient1: Colors.grey.shade800,
- gradient2: Colors.grey.shade900,
- child: const HeroIcon(HeroIcons.eye, color: Colors.white),
- ),
- ),
- const SizedBox(width: 10),
- WaitingButton(
- onTap: onDelete,
- builder: (child) => GroupButton(
- gradient1: ColorConstants.gradient1,
- gradient2: ColorConstants.gradient2,
- child: child,
- ),
- child: const HeroIcon(HeroIcons.xMark, color: Colors.white),
- ),
- ],
- ),
- ],
- );
- }
-}
diff --git a/lib/admin/ui/pages/groups/groups_page/groups_page.dart b/lib/admin/ui/pages/groups/groups_page/groups_page.dart
new file mode 100644
index 0000000000..d61438321a
--- /dev/null
+++ b/lib/admin/ui/pages/groups/groups_page/groups_page.dart
@@ -0,0 +1,315 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/class/simple_group.dart';
+import 'package:titan/admin/providers/group_id_provider.dart';
+import 'package:titan/admin/providers/group_list_provider.dart';
+import 'package:titan/admin/router.dart';
+import 'package:titan/navigation/ui/scroll_to_hide_navbar.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/styleguide/text_entry.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
+import 'package:titan/user/providers/user_list_provider.dart';
+import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
+
+class GroupsPage extends HookConsumerWidget {
+ const GroupsPage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final groups = ref.watch(allGroupListProvider);
+ final groupsNotifier = ref.watch(allGroupListProvider.notifier);
+ final nameController = useTextEditingController();
+ final descController = useTextEditingController();
+ final groupIdNotifier = ref.watch(groupIdProvider.notifier);
+ final groupListNotifier = ref.watch(allGroupListProvider.notifier);
+ final scrollController = useScrollController();
+ ref.watch(userList);
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+ final navigatorWithContext = Navigator.of(context);
+
+ return AdminTemplate(
+ child: ScrollToHideNavbar(
+ controller: scrollController,
+ child: Refresher(
+ controller: scrollController,
+ onRefresh: () async {
+ await groupsNotifier.loadGroups();
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
+ child: Column(
+ children: [
+ const SizedBox(height: 20),
+ Row(
+ children: [
+ Text(
+ localizeWithContext.adminGroupsManagement,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
+ ),
+ ),
+ Spacer(),
+ CustomIconButton(
+ icon: const HeroIcon(
+ HeroIcons.plus,
+ size: 30,
+ color: Colors.white,
+ ),
+ onPressed: () async {
+ nameController.text = '';
+ descController.text = '';
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminAddGroup,
+ child: Column(
+ children: [
+ TextEntry(
+ label: localizeWithContext.adminName,
+ controller: nameController,
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ label: localizeWithContext.adminDescription,
+ controller: descController,
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext.adminAdd,
+ onPressed: () async {
+ final addedGroupMsg = AppLocalizations.of(
+ context,
+ )!.adminAddedGroup;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.adminAddingError;
+ await tokenExpireWrapper(ref, () async {
+ final value = await groupListNotifier
+ .createGroup(
+ SimpleGroup(
+ name: nameController.text,
+ description: descController.text,
+ id: '',
+ ),
+ );
+ if (value) {
+ QR.back();
+ displayToastWithContext(
+ TypeMsg.msg,
+ addedGroupMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ addingErrorMsg,
+ );
+ }
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ const SizedBox(height: 10),
+ AsyncChild(
+ value: groups,
+ builder: (context, g) {
+ g.sort(
+ (a, b) =>
+ a.name.toLowerCase().compareTo(b.name.toLowerCase()),
+ );
+ return Column(
+ children: [
+ ...g.map(
+ (group) => Padding(
+ padding: const EdgeInsets.symmetric(vertical: 5.0),
+ child: ListItem(
+ title: group.name,
+ subtitle: group.description,
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: group.name,
+ child: Column(
+ children: [
+ Button(
+ text: localizeWithContext.adminEdit,
+ onPressed: () async {
+ nameController.text = group.name;
+ descController.text =
+ group.description;
+ Navigator.pop(context);
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext
+ .adminEditGroup,
+ child: Column(
+ children: [
+ TextEntry(
+ label: localizeWithContext
+ .adminName,
+ controller:
+ nameController,
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ label: localizeWithContext
+ .adminDescription,
+ controller:
+ descController,
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext
+ .adminEdit,
+ onPressed: () async {
+ final addedGroupMsg =
+ AppLocalizations.of(
+ context,
+ )!.adminAddedGroup;
+ final addingErrorMsg =
+ AppLocalizations.of(
+ context,
+ )!.adminAddingError;
+ await tokenExpireWrapper(
+ ref,
+ () async {
+ final value = await groupListNotifier
+ .updateGroup(
+ SimpleGroup(
+ name: nameController
+ .text,
+ description:
+ descController
+ .text,
+ id: group
+ .id,
+ ),
+ );
+ if (value) {
+ QR.back();
+ displayToastWithContext(
+ TypeMsg.msg,
+ addedGroupMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ addingErrorMsg,
+ );
+ }
+ },
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext
+ .adminManageMembers,
+ onPressed: () {
+ Navigator.pop(context);
+ groupIdNotifier.setId(group.id);
+ QR.to(
+ AdminRouter.root +
+ AdminRouter.usersGroups +
+ AdminRouter.editGroup,
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext
+ .adminDeleteGroup,
+ type: ButtonType.danger,
+ onPressed: () async {
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return CustomDialogBox(
+ title: localizeWithContext
+ .adminDelete,
+ descriptions: localizeWithContext
+ .adminDeleteGroupConfirmation,
+ onYes: () async {
+ tokenExpireWrapper(ref, () async {
+ final value =
+ await groupsNotifier
+ .deleteGroup(
+ group,
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext
+ .adminDeletedGroup,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext
+ .adminFailedToDeleteGroup,
+ );
+ }
+ });
+ },
+ );
+ },
+ );
+ navigatorWithContext.pop();
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/main_page/main_page.dart b/lib/admin/ui/pages/main_page/main_page.dart
index a458200eb1..e59220c545 100644
--- a/lib/admin/ui/pages/main_page/main_page.dart
+++ b/lib/admin/ui/pages/main_page/main_page.dart
@@ -1,13 +1,17 @@
import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/navigation/ui/scroll_to_hide_navbar.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/admin/admin.dart';
import 'package:titan/admin/router.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/pages/main_page/menu_card_ui.dart';
+import 'package:titan/admin/ui/pages/users_management_page/users_management_page.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+
import 'package:titan/user/providers/user_list_provider.dart';
-import 'package:qlevar_router/qlevar_router.dart';
class AdminMainPage extends HookConsumerWidget {
const AdminMainPage({super.key});
@@ -16,61 +20,102 @@ class AdminMainPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(userList);
- final controller = ScrollController();
+ final localizeWithContext = AppLocalizations.of(context)!;
return AdminTemplate(
child: Padding(
- padding: const EdgeInsets.all(40),
- child: GridView(
- controller: controller,
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- mainAxisSpacing: 20,
- crossAxisSpacing: 20,
- childAspectRatio:
- MediaQuery.of(context).size.width <
- MediaQuery.of(context).size.height
- ? 0.75
- : 1.5,
- ),
- children: [
- GestureDetector(
- onTap: () {
- QR.to(AdminRouter.root + AdminRouter.editModuleVisibility);
- },
- child: const MenuCardUi(
- text: AdminTextConstants.visibilities,
- icon: HeroIcons.eye,
- ),
+ padding: const EdgeInsets.all(20),
+ child: ScrollToHideNavbar(
+ controller: ScrollController(),
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ localizeWithContext.adminAdministration,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ localizeWithContext.adminUsersAndGroups,
+ style: Theme.of(context).textTheme.titleLarge,
+ ),
+ const SizedBox(height: 10),
+ ListItem(
+ title: localizeWithContext.adminUsersManagement,
+ subtitle: localizeWithContext.adminUsersManagementDescription,
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminUsersManagement,
+ child: UsersManagementPage(),
+ ),
+ );
+ },
+ ),
+ const SizedBox(height: 10),
+ ListItem(
+ title: localizeWithContext.adminGroupsManagement,
+ subtitle: localizeWithContext.adminManageUserGroups,
+ onTap: () =>
+ QR.to(AdminRouter.root + AdminRouter.usersGroups),
+ ),
+ const SizedBox(height: 10),
+ ListItem(
+ title: localizeWithContext.adminGroupNotification,
+ subtitle: localizeWithContext.adminSendNotificationToGroup,
+ onTap: () =>
+ QR.to(AdminRouter.root + AdminRouter.groupNotification),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ localizeWithContext.adminPaiementModule,
+ style: Theme.of(context).textTheme.titleLarge,
+ ),
+ const SizedBox(height: 10),
+ ListItem(
+ title: getPaymentName(),
+ subtitle: localizeWithContext.adminManagePaiementStructures,
+ onTap: () => QR.to(AdminRouter.root + AdminRouter.structures),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ localizeWithContext.adminAssociationMembership,
+ style: Theme.of(context).textTheme.titleLarge,
+ ),
+ const SizedBox(height: 10),
+ ListItem(
+ title: localizeWithContext.adminAssociationMembership,
+ subtitle: localizeWithContext
+ .adminManageUsersAssociationMemberships,
+ onTap: () => QR.to(
+ AdminRouter.root + AdminRouter.associationMemberships,
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ localizeWithContext.adminAssociations,
+ style: Theme.of(context).textTheme.titleLarge,
+ ),
+ const SizedBox(height: 10),
+ ListItem(
+ title: localizeWithContext.adminAssociations,
+ subtitle: localizeWithContext.adminManageAssociations,
+ onTap: () {
+ QR.to(AdminRouter.root + AdminRouter.association);
+ },
+ ),
+ const SizedBox(height: 20),
+ ],
),
- GestureDetector(
- onTap: () {
- QR.to(AdminRouter.root + AdminRouter.groups);
- },
- child: const MenuCardUi(
- text: AdminTextConstants.groups,
- icon: HeroIcons.users,
- ),
- ),
- GestureDetector(
- onTap: () {
- QR.to(AdminRouter.root + AdminRouter.schools);
- },
- child: const MenuCardUi(
- text: AdminTextConstants.schools,
- icon: HeroIcons.academicCap,
- ),
- ),
- GestureDetector(
- onTap: () {
- QR.to(AdminRouter.root + AdminRouter.structures);
- },
- child: const MenuCardUi(
- text: AdminTextConstants.myEclPay,
- icon: HeroIcons.creditCard,
- ),
- ),
- ],
+ ),
),
),
);
diff --git a/lib/admin/ui/pages/memberships/add_edit_user_membership_page/add_edit_user_membership_page.dart b/lib/admin/ui/pages/membership/add_edit_user_membership_page/add_edit_user_membership_page.dart
similarity index 57%
rename from lib/admin/ui/pages/memberships/add_edit_user_membership_page/add_edit_user_membership_page.dart
rename to lib/admin/ui/pages/membership/add_edit_user_membership_page/add_edit_user_membership_page.dart
index 3ec8fa38b3..854f2bd756 100644
--- a/lib/admin/ui/pages/memberships/add_edit_user_membership_page/add_edit_user_membership_page.dart
+++ b/lib/admin/ui/pages/membership/add_edit_user_membership_page/add_edit_user_membership_page.dart
@@ -1,41 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
+import 'package:titan/admin/admin.dart';
import 'package:titan/admin/class/user_association_membership.dart';
import 'package:titan/admin/class/user_association_membership_base.dart';
import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
import 'package:titan/admin/providers/user_association_membership_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/pages/memberships/add_edit_user_membership_page/search_result.dart';
+import 'package:titan/admin/ui/pages/membership/add_edit_user_membership_page/user_search_modal.dart';
import 'package:titan/tools/constants.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/tools/ui/layouts/add_edit_button_layout.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
import 'package:titan/tools/ui/widgets/align_left_text.dart';
import 'package:titan/tools/ui/widgets/date_entry.dart';
-import 'package:titan/tools/ui/widgets/styled_search_bar.dart';
-import 'package:titan/user/providers/user_list_provider.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditUserMembershipPage extends HookConsumerWidget {
const AddEditUserMembershipPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final associationMembershipMembersNotifier = ref.watch(
associationMembershipMembersProvider.notifier,
);
- final queryController = useTextEditingController(text: '');
- final usersNotifier = ref.watch(userList.notifier);
final membership = ref.watch(userAssociationMembershipProvider);
final isEdit = membership.id != UserAssociationMembership.empty().id;
final start = useTextEditingController(
- text: isEdit ? processDate(membership.startDate) : "",
+ text: isEdit ? DateFormat.yMd(locale).format(membership.startDate) : "",
);
final end = useTextEditingController(
- text: isEdit ? processDate(membership.endDate) : "",
+ text: isEdit ? DateFormat.yMd(locale).format(membership.endDate) : "",
);
void displayToastWithContext(TypeMsg type, String msg) {
@@ -44,32 +43,32 @@ class AddEditUserMembershipPage extends HookConsumerWidget {
return AdminTemplate(
child: Padding(
- padding: const EdgeInsets.all(30.0),
+ padding: const EdgeInsets.all(20.0),
child: SingleChildScrollView(
child: Column(
children: [
AlignLeftText(
isEdit
- ? AdminTextConstants.editMembership
- : AdminTextConstants.addMember,
+ ? AppLocalizations.of(context)!.adminEditMembership
+ : AppLocalizations.of(context)!.adminAddMember,
+ fontWeight: FontWeight.w900,
+ color: ColorConstants.title,
+ fontSize: 24,
),
const SizedBox(height: 20),
if (!isEdit) ...[
- StyledSearchBar(
- padding: EdgeInsets.zero,
- label: AdminTextConstants.user,
- editingController: queryController,
- onChanged: (value) async {
- tokenExpireWrapper(ref, () async {
- if (value.isNotEmpty) {
- await usersNotifier.filterUsers(value);
- } else {
- usersNotifier.clear();
- }
- });
+ ListItem(
+ title: membership.user.id.isNotEmpty
+ ? membership.user.getName()
+ : AppLocalizations.of(context)!.adminUser,
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: UserSearchModal(),
+ );
},
),
- SearchResult(queryController: queryController),
] else
Text(
membership.user.getName(),
@@ -80,7 +79,7 @@ class AddEditUserMembershipPage extends HookConsumerWidget {
),
const SizedBox(height: 10),
DateEntry(
- label: AdminTextConstants.startDate,
+ label: AppLocalizations.of(context)!.adminStartDate,
controller: start,
onTap: () => getOnlyDayDate(
context,
@@ -89,9 +88,9 @@ class AddEditUserMembershipPage extends HookConsumerWidget {
lastDate: DateTime(2100),
),
),
- const SizedBox(height: 50),
+ const SizedBox(height: 10),
DateEntry(
- label: AdminTextConstants.endDate,
+ label: AppLocalizations.of(context)!.adminEndDate,
controller: end,
onTap: () => getOnlyDayDate(
context,
@@ -100,17 +99,25 @@ class AddEditUserMembershipPage extends HookConsumerWidget {
lastDate: DateTime(2100),
),
),
- const SizedBox(height: 50),
+ const SizedBox(height: 20),
WaitingButton(
- builder: (child) => AddEditButtonLayout(
- colors: const [
- ColorConstants.gradient1,
- ColorConstants.gradient2,
- ],
- child: child,
+ builder: (child) => Container(
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 20,
+ vertical: 10,
+ ),
+ decoration: BoxDecoration(
+ color: ColorConstants.tertiary,
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: ColorConstants.onTertiary),
+ ),
+ child: Center(child: child),
),
child: Text(
- !isEdit ? AdminTextConstants.add : AdminTextConstants.edit,
+ !isEdit
+ ? AppLocalizations.of(context)!.adminAdd
+ : AppLocalizations.of(context)!.adminEdit,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
@@ -121,50 +128,60 @@ class AddEditUserMembershipPage extends HookConsumerWidget {
if (membership.user.id == "") {
displayToastWithContext(
TypeMsg.msg,
- AdminTextConstants.emptyUser,
+ AppLocalizations.of(context)!.adminEmptyUser,
);
return;
}
if (start.text.isEmpty || end.text.isEmpty) {
displayToastWithContext(
TypeMsg.msg,
- AdminTextConstants.emptyDate,
+ AppLocalizations.of(context)!.adminEmptyDate,
);
return;
}
tokenExpireWrapper(ref, () async {
if (DateTime.parse(
- processDateBack(start.text),
- ).isAfter(DateTime.parse(processDateBack(end.text)))) {
+ processDateBack(start.text, locale.toString()),
+ ).isAfter(
+ DateTime.parse(
+ processDateBack(end.text, locale.toString()),
+ ),
+ )) {
displayToastWithContext(
TypeMsg.error,
- AdminTextConstants.dateError,
+ AppLocalizations.of(context)!.adminDateError,
);
return;
}
if (isEdit) {
+ final updatedMembershipMsg = AppLocalizations.of(
+ context,
+ )!.adminUpdatedMembership;
+ final updatingErrorMsg = AppLocalizations.of(
+ context,
+ )!.adminMembershipUpdatingError;
final value = await associationMembershipMembersNotifier
.updateMember(
membership.copyWith(
startDate: DateTime.parse(
- processDateBack(start.text),
+ processDateBack(start.text, locale.toString()),
),
endDate: DateTime.parse(
- processDateBack(end.text),
+ processDateBack(end.text, locale.toString()),
),
),
);
if (value) {
displayToastWithContext(
TypeMsg.msg,
- AdminTextConstants.updatedMembership,
+ updatedMembershipMsg,
);
QR.back();
} else {
displayToastWithContext(
TypeMsg.error,
- AdminTextConstants.membershipUpdatingError,
+ updatingErrorMsg,
);
}
} else {
@@ -174,22 +191,26 @@ class AddEditUserMembershipPage extends HookConsumerWidget {
associationMembershipId:
membership.associationMembershipId,
userId: membership.user.id,
- startDate: DateTime.parse(processDateBack(start.text)),
- endDate: DateTime.parse(processDateBack(end.text)),
+ startDate: DateTime.parse(
+ processDateBack(start.text, locale.toString()),
+ ),
+ endDate: DateTime.parse(
+ processDateBack(end.text, locale.toString()),
+ ),
);
+ final addedMemberMsg = AppLocalizations.of(
+ context,
+ )!.adminAddedMember;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.adminMembershipAddingError;
final value = await associationMembershipMembersNotifier
.addMember(membershipAdd, membership.user);
if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.addedMember,
- );
+ displayToastWithContext(TypeMsg.msg, addedMemberMsg);
QR.back();
} else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.membershipAddingError,
- );
+ displayToastWithContext(TypeMsg.error, addingErrorMsg);
}
}
});
diff --git a/lib/admin/ui/pages/membership/add_edit_user_membership_page/search_result.dart b/lib/admin/ui/pages/membership/add_edit_user_membership_page/search_result.dart
new file mode 100644
index 0000000000..281d4e5743
--- /dev/null
+++ b/lib/admin/ui/pages/membership/add_edit_user_membership_page/search_result.dart
@@ -0,0 +1,58 @@
+import 'package:flutter/material.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/user_association_membership_provider.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/user/providers/user_list_provider.dart';
+
+class SearchResult extends HookConsumerWidget {
+ final TextEditingController queryController;
+ const SearchResult({super.key, required this.queryController});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final users = ref.watch(userList);
+ final usersNotifier = ref.watch(userList.notifier);
+ final membershipNotifier = ref.watch(
+ userAssociationMembershipProvider.notifier,
+ );
+ final membership = ref.watch(userAssociationMembershipProvider);
+
+ return AsyncChild(
+ value: users,
+ builder: (context, usersData) {
+ return Column(
+ children: usersData
+ .map(
+ (user) => Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Text(
+ user.getName(),
+ style: const TextStyle(fontSize: 15),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ GestureDetector(
+ onTap: () {
+ membershipNotifier.setUserAssociationMembership(
+ membership.copyWith(user: user, userId: user.id),
+ );
+ usersNotifier.clear();
+ Navigator.of(context).pop();
+ },
+ child: const HeroIcon(HeroIcons.plus),
+ ),
+ ],
+ ),
+ ),
+ )
+ .toList(),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/membership/add_edit_user_membership_page/user_search_modal.dart b/lib/admin/ui/pages/membership/add_edit_user_membership_page/user_search_modal.dart
new file mode 100644
index 0000000000..20b3195f97
--- /dev/null
+++ b/lib/admin/ui/pages/membership/add_edit_user_membership_page/user_search_modal.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/ui/pages/membership/add_edit_user_membership_page/search_result.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/searchbar.dart';
+import 'package:titan/user/providers/user_list_provider.dart';
+
+class UserSearchModal extends HookConsumerWidget {
+ const UserSearchModal({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final usersNotifier = ref.watch(userList.notifier);
+ final textController = useTextEditingController();
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return BottomModalTemplate(
+ title: localizeWithContext.adminSelectManager,
+ type: BottomModalType.main,
+ child: Column(
+ children: [
+ CustomSearchBar(
+ autofocus: true,
+ onSearch: (value) => tokenExpireWrapper(ref, () async {
+ if (value.isNotEmpty) {
+ await usersNotifier.filterUsers(value);
+ textController.text = value;
+ } else {
+ usersNotifier.clear();
+ textController.clear();
+ }
+ }),
+ ),
+ const SizedBox(height: 10),
+ ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 280),
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: SearchResult(queryController: textController),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_detail_page.dart b/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_detail_page.dart
similarity index 61%
rename from lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_detail_page.dart
rename to lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_detail_page.dart
index 87b343fff3..53349aa6b6 100644
--- a/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_detail_page.dart
+++ b/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_detail_page.dart
@@ -1,22 +1,25 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/providers/research_filter_provider.dart';
+import 'package:titan/admin/router.dart';
import 'package:titan/admin/class/user_association_membership.dart';
import 'package:titan/admin/providers/association_membership_filtered_members_provider.dart';
import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
import 'package:titan/admin/providers/association_membership_provider.dart';
import 'package:titan/admin/providers/user_association_membership_provider.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_detail_page/association_membership_information_editor.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_detail_page/association_membership_member_editable_card.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_detail_page/research_bar.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_detail_page/search_filters.dart';
+import 'package:titan/admin/ui/pages/membership/association_membership_detail_page/association_membership_information_editor.dart';
+import 'package:titan/admin/ui/pages/membership/association_membership_detail_page/association_membership_member_editable_card.dart';
+import 'package:titan/admin/ui/pages/membership/association_membership_detail_page/search_filters.dart';
import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/styleguide/searchbar.dart';
class AssociationMembershipEditorPage extends HookConsumerWidget {
final scrollKey = GlobalKey();
@@ -24,6 +27,7 @@ class AssociationMembershipEditorPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final filterNotifier = ref.watch(filterProvider.notifier);
final associationMembership = ref.watch(associationMembershipProvider);
final associationMembershipMemberListNotifier = ref.watch(
associationMembershipMembersProvider.notifier,
@@ -37,23 +41,24 @@ class AssociationMembershipEditorPage extends HookConsumerWidget {
return AdminTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await associationMembershipMemberListNotifier
.loadAssociationMembershipMembers(associationMembership.id);
},
child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30),
+ padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
const SizedBox(height: 20),
Container(
alignment: Alignment.centerLeft,
child: Text(
- "${AdminTextConstants.associationMembership} ${associationMembership.name}",
+ "${AppLocalizations.of(context)!.adminAssociationMembership} ${associationMembership.name}",
style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
),
),
),
@@ -61,35 +66,17 @@ class AssociationMembershipEditorPage extends HookConsumerWidget {
const SizedBox(height: 30),
Row(
children: [
- const Text(
- AdminTextConstants.members,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
- ),
- ),
- const SizedBox(width: 10),
Text(
- "(${associationMembershipFilteredList.length} ${AdminTextConstants.members})",
- style: const TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
+ "${AppLocalizations.of(context)!.adminMembers} (${associationMembershipFilteredList.length})",
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
),
),
const Spacer(),
- WaitingButton(
- builder: (child) => Container(
- width: 40,
- height: 40,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(10),
- color: ColorConstants.gradient1,
- ),
- child: child,
- ),
- onTap: () async {
+ CustomIconButton(
+ onPressed: () async {
userAssociationMembershipNotifier
.setUserAssociationMembership(
UserAssociationMembership.empty().copyWith(
@@ -103,7 +90,7 @@ class AssociationMembershipEditorPage extends HookConsumerWidget {
AdminRouter.addEditMember,
);
},
- child: const HeroIcon(
+ icon: const HeroIcon(
HeroIcons.plus,
size: 30,
color: Colors.white,
@@ -112,15 +99,36 @@ class AssociationMembershipEditorPage extends HookConsumerWidget {
],
),
const SizedBox(height: 10),
- ExpansionTile(
- title: const Text(AdminTextConstants.filters),
- children: const [SearchFilters()],
+ ListItem(
+ title: AppLocalizations.of(context)!.adminFilters,
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: AppLocalizations.of(context)!.adminFilters,
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: SearchFilters(),
+ ),
+ ),
+ );
+ },
),
const SizedBox(height: 20),
- ResearchBar(),
+ CustomSearchBar(
+ onSearch: (query) {
+ filterNotifier.setFilter(query);
+ },
+ ),
const SizedBox(height: 10),
associationMembershipFilteredList.isEmpty
- ? const Text(AdminTextConstants.noMember)
+ ? Text(AppLocalizations.of(context)!.adminNoMember)
: SizedBox(
height: 400,
child: ListView.builder(
diff --git a/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_information_editor.dart b/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_information_editor.dart
new file mode 100644
index 0000000000..eef898f45f
--- /dev/null
+++ b/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_information_editor.dart
@@ -0,0 +1,209 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/all_groups_list_provider.dart';
+import 'package:titan/admin/providers/association_membership_list_provider.dart';
+import 'package:titan/admin/providers/association_membership_provider.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+
+class AssociationMembershipInformationEditor extends HookConsumerWidget {
+ final scrollKey = GlobalKey();
+ AssociationMembershipInformationEditor({super.key});
+
+ @override
+ Widget build(context, ref) {
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ final associationMembership = ref.watch(associationMembershipProvider);
+ final associationMembershipNotifier = ref.watch(
+ associationMembershipProvider.notifier,
+ );
+ final name = useTextEditingController(text: associationMembership.name);
+ final groups = ref.watch(allGroupList);
+ final groupIdController = useTextEditingController(
+ text: associationMembership.managerGroupId,
+ );
+ final associationMembershipListNotifier = ref.watch(
+ allAssociationMembershipListProvider.notifier,
+ );
+ final key = GlobalKey();
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ groups.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
+
+ return Form(
+ key: key,
+ child: Column(
+ children: [
+ Container(
+ margin: const EdgeInsets.symmetric(vertical: 10),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(
+ child: TextFormField(
+ controller: name,
+ cursorColor: ColorConstants.tertiary,
+ decoration: InputDecoration(
+ labelText: AppLocalizations.of(context)!.adminName,
+ labelStyle: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ suffixIcon: Container(
+ margin: const EdgeInsets.only(left: 20),
+ child: const HeroIcon(HeroIcons.pencil),
+ ),
+ enabledBorder: const UnderlineInputBorder(
+ borderSide: BorderSide(color: Colors.transparent),
+ ),
+ focusedBorder: const UnderlineInputBorder(
+ borderSide: BorderSide(color: ColorConstants.tertiary),
+ ),
+ ),
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return AppLocalizations.of(
+ context,
+ )!.adminEmptyFieldError;
+ }
+ return null;
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ AppLocalizations.of(context)!.adminGroup,
+ style: const TextStyle(fontWeight: FontWeight.bold),
+ ),
+ ),
+ ListItem(
+ title: groups
+ .firstWhere((group) => group.id == groupIdController.text)
+ .name,
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminChooseGroupManager,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 280),
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: Column(
+ children: [
+ ...groups.map(
+ (e) => Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 8.0,
+ ),
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Text(
+ e.name,
+ style: const TextStyle(fontSize: 15),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ GestureDetector(
+ onTap: () {
+ groupIdController.text = e.id;
+ Navigator.of(ctx).pop();
+ },
+ child: const HeroIcon(HeroIcons.plus),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ WaitingButton(
+ builder: (child) => Container(
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
+ decoration: BoxDecoration(
+ color: ColorConstants.tertiary,
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: ColorConstants.onTertiary),
+ ),
+ child: Center(child: child),
+ ),
+ onTap: () async {
+ if (!key.currentState!.validate()) {
+ return;
+ }
+
+ await tokenExpireWrapper(ref, () async {
+ final updatedAssociationMembershipMsg = AppLocalizations.of(
+ context,
+ )!.adminUpdatedAssociationMembership;
+ final updatingAssociationMembershipErrorMsg =
+ AppLocalizations.of(context)!.adminUpdatingError;
+ final value = await associationMembershipListNotifier
+ .updateAssociationMembership(
+ associationMembership.copyWith(name: name.text),
+ );
+ if (value) {
+ associationMembershipNotifier.setAssociationMembership(
+ associationMembership.copyWith(
+ name: name.text,
+ managerGroupId: groupIdController.text,
+ ),
+ );
+ displayToastWithContext(
+ TypeMsg.msg,
+ updatedAssociationMembershipMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.msg,
+ updatingAssociationMembershipErrorMsg,
+ );
+ }
+ });
+ },
+ child: Text(
+ AppLocalizations.of(context)!.adminEdit,
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Color.fromARGB(255, 255, 255, 255),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_member_editable_card.dart b/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_member_editable_card.dart
new file mode 100644
index 0000000000..b4c7e3360f
--- /dev/null
+++ b/lib/admin/ui/pages/membership/association_membership_detail_page/association_membership_member_editable_card.dart
@@ -0,0 +1,144 @@
+import 'package:auto_size_text/auto_size_text.dart';
+import 'package:flutter/material.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
+import 'package:titan/admin/class/user_association_membership.dart';
+import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
+import 'package:titan/admin/providers/user_association_membership_provider.dart';
+import 'package:titan/admin/router.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/providers/locale_notifier.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
+
+class MemberEditableCard extends HookConsumerWidget {
+ const MemberEditableCard({super.key, required this.associationMembership});
+
+ final UserAssociationMembership associationMembership;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final locale = ref.watch(localeProvider);
+ final associationMembershipMemberListNotifier = ref.watch(
+ associationMembershipMembersProvider.notifier,
+ );
+ final userAssociationMembershipNotifier = ref.watch(
+ userAssociationMembershipProvider.notifier,
+ );
+
+ final localization = AppLocalizations.of(context)!;
+
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ return Container(
+ padding: const EdgeInsets.symmetric(vertical: 5),
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ AutoSizeText(
+ (associationMembership.user.nickname ??
+ "${associationMembership.user.firstname} ${associationMembership.user.name}"),
+ style: const TextStyle(fontWeight: FontWeight.bold),
+ minFontSize: 10,
+ maxFontSize: 15,
+ overflow: TextOverflow.ellipsis,
+ ),
+ associationMembership.user.nickname != null
+ ? AutoSizeText(
+ "${associationMembership.user.firstname} ${associationMembership.user.name}",
+ minFontSize: 10,
+ maxFontSize: 15,
+ overflow: TextOverflow.ellipsis,
+ )
+ : const SizedBox(),
+ ],
+ ),
+ ),
+ Expanded(
+ child: Column(
+ children: [
+ Text(
+ DateFormat.yMd(
+ locale.toString(),
+ ).format(associationMembership.startDate),
+ style: const TextStyle(fontSize: 12),
+ ),
+ Text(
+ DateFormat.yMd(
+ locale.toString(),
+ ).format(associationMembership.endDate),
+ style: const TextStyle(fontSize: 12),
+ ),
+ ],
+ ),
+ ),
+ CustomIconButton.secondary(
+ icon: const HeroIcon(
+ HeroIcons.pencil,
+ color: ColorConstants.tertiary,
+ ),
+ onPressed: () async {
+ userAssociationMembershipNotifier.setUserAssociationMembership(
+ associationMembership,
+ );
+ QR.to(
+ AdminRouter.root +
+ AdminRouter.associationMemberships +
+ AdminRouter.detailAssociationMembership +
+ AdminRouter.addEditMember,
+ );
+ },
+ ),
+ const SizedBox(width: 10),
+ CustomIconButton.danger(
+ icon: HeroIcon(HeroIcons.trash, color: Colors.white),
+ onPressed: () async {
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return CustomDialogBox(
+ title: localization.adminDeleteAssociationMember,
+ descriptions:
+ localization.adminDeleteAssociationMemberConfirmation,
+ onYes: () async {
+ final deletedMemberMsg =
+ localization.phonebookDeletedMember;
+ final deleteMemberErrorMsg =
+ localization.phonebookDeletingError;
+ await tokenExpireWrapper(ref, () async {
+ final result =
+ await associationMembershipMemberListNotifier
+ .deleteMember(associationMembership);
+ if (result) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ deletedMemberMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ deleteMemberErrorMsg,
+ );
+ }
+ });
+ },
+ );
+ },
+ );
+ },
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/membership/association_membership_detail_page/search_filters.dart b/lib/admin/ui/pages/membership/association_membership_detail_page/search_filters.dart
new file mode 100644
index 0000000000..09c08d5e5c
--- /dev/null
+++ b/lib/admin/ui/pages/membership/association_membership_detail_page/search_filters.dart
@@ -0,0 +1,176 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
+import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
+import 'package:titan/admin/providers/association_membership_provider.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/tools/ui/layouts/add_edit_button_layout.dart';
+import 'package:titan/tools/ui/widgets/date_entry.dart';
+import 'package:titan/l10n/app_localizations.dart';
+
+class SearchFilters extends HookConsumerWidget {
+ const SearchFilters({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
+ final associationMembershipMemberListNotifier = ref.watch(
+ associationMembershipMembersProvider.notifier,
+ );
+ final associationMembership = ref.watch(associationMembershipProvider);
+ final startMinimal = useTextEditingController(text: "");
+ final startMaximal = useTextEditingController(
+ text: DateFormat.yMd(locale).format(DateTime.now()),
+ );
+ final endMinimal = useTextEditingController(
+ text: DateFormat.yMd(locale).format(DateTime.now()),
+ );
+ final endMaximal = useTextEditingController(text: "");
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.adminStartDate,
+ style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900),
+ ),
+ const SizedBox(height: 10),
+ DateEntry(
+ label: AppLocalizations.of(context)!.adminStartDateMinimal,
+ controller: startMinimal,
+ onTap: () => getOnlyDayDate(
+ context,
+ startMinimal,
+ firstDate: DateTime(2019),
+ lastDate: DateTime(DateTime.now().year + 7),
+ ),
+ ),
+ const SizedBox(height: 10),
+ DateEntry(
+ label: AppLocalizations.of(context)!.adminStartDateMaximal,
+ controller: startMaximal,
+ onTap: () => getOnlyDayDate(
+ context,
+ startMaximal,
+ firstDate: DateTime(2019),
+ lastDate: DateTime(DateTime.now().year + 7),
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ AppLocalizations.of(context)!.adminEndDate,
+ style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900),
+ ),
+ const SizedBox(height: 10),
+ DateEntry(
+ label: AppLocalizations.of(context)!.adminEndDateMinimal,
+ controller: endMinimal,
+ onTap: () => getOnlyDayDate(
+ context,
+ endMinimal,
+ firstDate: DateTime(2019),
+ lastDate: DateTime(DateTime.now().year + 7),
+ ),
+ ),
+ const SizedBox(height: 10),
+ DateEntry(
+ label: AppLocalizations.of(context)!.adminEndDateMaximal,
+ controller: endMaximal,
+ onTap: () => getOnlyDayDate(
+ context,
+ endMaximal,
+ firstDate: DateTime(2019),
+ lastDate: DateTime(DateTime.now().year + 7),
+ ),
+ ),
+ const SizedBox(height: 30),
+ WaitingButton(
+ onTap: () async {
+ await tokenExpireWrapper(ref, () async {
+ await associationMembershipMemberListNotifier
+ .loadAssociationMembershipMembers(
+ associationMembership.id,
+ minimalStartDate: startMinimal.text.isNotEmpty
+ ? DateTime.parse(
+ processDateBack(
+ startMinimal.text,
+ locale.toString(),
+ ),
+ )
+ : null,
+ minimalEndDate: endMinimal.text.isNotEmpty
+ ? DateTime.parse(
+ processDateBack(endMinimal.text, locale.toString()),
+ )
+ : null,
+ maximalStartDate: startMaximal.text.isNotEmpty
+ ? DateTime.parse(
+ processDateBack(
+ startMaximal.text,
+ locale.toString(),
+ ),
+ )
+ : null,
+ maximalEndDate: endMaximal.text.isNotEmpty
+ ? DateTime.parse(
+ processDateBack(endMaximal.text, locale.toString()),
+ )
+ : null,
+ );
+ });
+ },
+ builder: (child) => Container(
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
+ decoration: BoxDecoration(
+ color: ColorConstants.tertiary,
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: ColorConstants.onTertiary),
+ ),
+ child: Center(child: child),
+ ),
+ child: Text(
+ AppLocalizations.of(context)!.adminValidateFilters,
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Color.fromARGB(255, 255, 255, 255),
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ SizedBox(height: 20),
+ WaitingButton(
+ onTap: () async {
+ startMaximal.clear();
+ startMinimal.clear();
+ endMaximal.clear();
+ endMinimal.clear();
+ await tokenExpireWrapper(ref, () async {
+ await associationMembershipMemberListNotifier
+ .loadAssociationMembershipMembers(associationMembership.id);
+ });
+ },
+ builder: (child) => AddEditButtonLayout(
+ colors: const [ColorConstants.main, ColorConstants.onMain],
+ child: child,
+ ),
+ child: Text(
+ AppLocalizations.of(context)!.adminClearFilters,
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Color.fromARGB(255, 255, 255, 255),
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ SizedBox(height: 10),
+ ],
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/membership/association_membership_page/add_membership_modal.dart b/lib/admin/ui/pages/membership/association_membership_page/add_membership_modal.dart
new file mode 100644
index 0000000000..b126b4ea80
--- /dev/null
+++ b/lib/admin/ui/pages/membership/association_membership_page/add_membership_modal.dart
@@ -0,0 +1,116 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:titan/admin/class/simple_group.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/widgets/text_entry.dart';
+
+class AddMembershipModal extends HookWidget {
+ final List groups;
+ final void Function(SimpleGroup group, String name) onSubmit;
+ final WidgetRef ref;
+
+ const AddMembershipModal({
+ super.key,
+ required this.groups,
+ required this.onSubmit,
+ required this.ref,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final nameController = useTextEditingController();
+ final chosenGroup = useState(null);
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return BottomModalTemplate(
+ title: localizeWithContext.adminAssociationMembershipsManagement,
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: TextEntry(
+ label: localizeWithContext.adminName,
+ controller: nameController,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: ListItem(
+ title: chosenGroup.value == null
+ ? localizeWithContext.adminChooseGroupManager
+ : chosenGroup.value!.name,
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminChooseGroupManager,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 280),
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: Column(
+ children: [
+ ...groups.map(
+ (e) => Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 8.0,
+ ),
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Text(
+ e.name,
+ style: const TextStyle(fontSize: 15),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ GestureDetector(
+ onTap: () {
+ chosenGroup.value = e;
+ Navigator.of(ctx).pop();
+ },
+ child: const HeroIcon(HeroIcons.plus),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ const SizedBox(height: 10),
+ Button(
+ text: localizeWithContext.adminAdd,
+ onPressed: () {
+ if (chosenGroup.value != null) {
+ onSubmit(chosenGroup.value!, nameController.text);
+ }
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/membership/association_membership_page/association_membership_page.dart b/lib/admin/ui/pages/membership/association_membership_page/association_membership_page.dart
new file mode 100644
index 0000000000..79ab71b5f9
--- /dev/null
+++ b/lib/admin/ui/pages/membership/association_membership_page/association_membership_page.dart
@@ -0,0 +1,238 @@
+import 'package:flutter/material.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/class/association_membership_simple.dart';
+import 'package:titan/admin/providers/all_groups_list_provider.dart';
+import 'package:titan/admin/providers/association_membership_list_provider.dart';
+import 'package:titan/admin/router.dart';
+import 'package:titan/admin/ui/pages/membership/association_membership_page/add_membership_modal.dart';
+import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
+import 'package:titan/admin/providers/association_membership_provider.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
+
+class AssociationMembershipsPage extends HookConsumerWidget {
+ const AssociationMembershipsPage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final associationsMemberships = ref.watch(
+ allAssociationMembershipListProvider,
+ );
+ final associationMembershipsNotifier = ref.watch(
+ allAssociationMembershipListProvider.notifier,
+ );
+ final associationMembershipNotifier = ref.watch(
+ associationMembershipProvider.notifier,
+ );
+ final associationMembershipMembersNotifier = ref.watch(
+ associationMembershipMembersProvider.notifier,
+ );
+ final groups = ref.watch(allGroupList);
+
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ void popWithContext() {
+ Navigator.of(context).pop();
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return AdminTemplate(
+ child: Refresher(
+ controller: ScrollController(),
+ onRefresh: () async {
+ await associationMembershipsNotifier.loadAssociationMemberships();
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
+ child: Column(
+ children: [
+ const SizedBox(height: 20),
+ Row(
+ children: [
+ Text(
+ AppLocalizations.of(context)!.adminAssociationMembership,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
+ ),
+ ),
+ const Spacer(),
+ CustomIconButton(
+ icon: HeroIcon(
+ HeroIcons.plus,
+ color: Colors.white,
+ size: 30,
+ ),
+ onPressed: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: AddMembershipModal(
+ ref: ref,
+ groups: groups,
+ onSubmit: (group, name) {
+ tokenExpireWrapper(ref, () async {
+ final value = await associationMembershipsNotifier
+ .createAssociationMembership(
+ AssociationMembership.empty().copyWith(
+ managerGroupId: group.id,
+ name: name,
+ ),
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext
+ .adminCreatedAssociationMembership,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext.adminCreationError,
+ );
+ }
+ popWithContext();
+ });
+ },
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ SizedBox(height: 10),
+ AsyncChild(
+ value: associationsMemberships,
+ builder: (context, g) {
+ g.sort(
+ (a, b) =>
+ a.name.toLowerCase().compareTo(b.name.toLowerCase()),
+ );
+ return Column(
+ children: [
+ Column(
+ children: [
+ ...g.map(
+ (associationMembership) => Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 5.0,
+ ),
+ child: ListItem(
+ title: associationMembership.name,
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: associationMembership.name,
+ child: Column(
+ children: [
+ Button(
+ text: localizeWithContext.adminEdit,
+ onPressed: () {
+ associationMembershipMembersNotifier
+ .loadAssociationMembershipMembers(
+ associationMembership.id,
+ );
+ associationMembershipNotifier
+ .setAssociationMembership(
+ associationMembership,
+ );
+ QR.to(
+ AdminRouter.root +
+ AdminRouter
+ .associationMemberships +
+ AdminRouter
+ .detailAssociationMembership,
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text:
+ localizeWithContext.adminDelete,
+ type: ButtonType.danger,
+ onPressed: () async {
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return CustomDialogBox(
+ title: AppLocalizations.of(
+ context,
+ )!.adminDeleting,
+ descriptions:
+ AppLocalizations.of(
+ context,
+ )!.adminDeleteAssociationMembership,
+ onYes: () async {
+ tokenExpireWrapper(ref, () async {
+ final deletedAssociationMembershipMsg =
+ AppLocalizations.of(
+ context,
+ )!.adminDeletedAssociationMembership;
+ final deletingErrorMsg =
+ AppLocalizations.of(
+ context,
+ )!.adminDeletingError;
+ final value =
+ await associationMembershipsNotifier
+ .deleteAssociationMembership(
+ associationMembership,
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ deletedAssociationMembershipMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ deletingErrorMsg,
+ );
+ }
+ });
+ },
+ );
+ },
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ],
+ );
+ },
+ loaderColor: ColorConstants.main,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/memberships/add_edit_user_membership_page/search_result.dart b/lib/admin/ui/pages/memberships/add_edit_user_membership_page/search_result.dart
deleted file mode 100644
index e9d0994a6b..0000000000
--- a/lib/admin/ui/pages/memberships/add_edit_user_membership_page/search_result.dart
+++ /dev/null
@@ -1,75 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/user_association_membership_provider.dart';
-import 'package:titan/user/providers/user_list_provider.dart';
-
-class SearchResult extends HookConsumerWidget {
- final TextEditingController queryController;
- const SearchResult({super.key, required this.queryController});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final users = ref.watch(userList);
- final usersNotifier = ref.watch(userList.notifier);
- final membershipNotifier = ref.watch(
- userAssociationMembershipProvider.notifier,
- );
- final membership = ref.watch(userAssociationMembershipProvider);
-
- return users.when(
- data: (usersData) {
- return Column(
- children: usersData
- .map(
- (user) => GestureDetector(
- behavior: HitTestBehavior.opaque,
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 4.0),
- child: Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(10),
- color: Colors.white,
- boxShadow: [
- BoxShadow(
- color: Colors.black.withValues(alpha: 0.1),
- offset: const Offset(0, 1),
- blurRadius: 4,
- spreadRadius: 2,
- ),
- ],
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 14),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Container(width: 20),
- Expanded(
- child: Text(
- user.getName(),
- style: const TextStyle(fontSize: 13),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- onTap: () {
- membershipNotifier.setUserAssociationMembership(
- membership.copyWith(user: user, userId: user.id),
- );
- queryController.text = user.getName();
- usersNotifier.clear();
- },
- ),
- )
- .toList(),
- );
- },
- loading: () => const Center(child: CircularProgressIndicator()),
- error: (e, s) => Text(e.toString()),
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_information_editor.dart b/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_information_editor.dart
deleted file mode 100644
index 1b19086843..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_information_editor.dart
+++ /dev/null
@@ -1,164 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/all_groups_list_provider.dart';
-import 'package:titan/admin/providers/association_membership_list_provider.dart';
-import 'package:titan/admin/providers/association_membership_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/tools/ui/layouts/add_edit_button_layout.dart';
-
-class AssociationMembershipInformationEditor extends HookConsumerWidget {
- final scrollKey = GlobalKey();
- AssociationMembershipInformationEditor({super.key});
-
- @override
- Widget build(context, ref) {
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- final associationMembership = ref.watch(associationMembershipProvider);
- final associationMembershipNotifier = ref.watch(
- associationMembershipProvider.notifier,
- );
- final name = useTextEditingController(text: associationMembership.name);
- final groups = ref.watch(allGroupList);
- final groupIdController = useTextEditingController(
- text: associationMembership.managerGroupId,
- );
- final associationMembershipListNotifier = ref.watch(
- allAssociationMembershipListProvider.notifier,
- );
- final key = GlobalKey();
-
- groups.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
-
- return Column(
- children: [
- Form(
- key: key,
- child: Column(
- children: [
- Container(
- margin: const EdgeInsets.symmetric(vertical: 10),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(
- child: TextFormField(
- controller: name,
- cursorColor: ColorConstants.gradient1,
- decoration: InputDecoration(
- labelText: AdminTextConstants.name,
- labelStyle: const TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
- suffixIcon: Container(
- padding: const EdgeInsets.all(10),
- child: const HeroIcon(HeroIcons.pencil),
- ),
- enabledBorder: const UnderlineInputBorder(
- borderSide: BorderSide(color: Colors.transparent),
- ),
- focusedBorder: const UnderlineInputBorder(
- borderSide: BorderSide(
- color: ColorConstants.gradient1,
- ),
- ),
- ),
- validator: (value) {
- if (value == null || value.isEmpty) {
- return AdminTextConstants.emptyFieldError;
- }
- return null;
- },
- ),
- ),
- ],
- ),
- ),
- const Align(
- alignment: Alignment.centerLeft,
- child: Text(
- AdminTextConstants.group,
- style: TextStyle(fontWeight: FontWeight.bold),
- ),
- ),
- DropdownButtonFormField(
- value: groupIdController.text,
- onChanged: (String? newValue) {
- groupIdController.text = newValue!;
- },
- items: groups
- .map(
- (group) => DropdownMenuItem(
- value: group.id,
- child: Text(group.name),
- ),
- )
- .toList(),
- decoration: const InputDecoration(
- hintText: AdminTextConstants.group,
- ),
- ),
- const SizedBox(height: 20),
- WaitingButton(
- builder: (child) => AddEditButtonLayout(
- colors: const [
- ColorConstants.gradient1,
- ColorConstants.gradient2,
- ],
- child: child,
- ),
- onTap: () async {
- if (!key.currentState!.validate()) {
- return;
- }
-
- await tokenExpireWrapper(ref, () async {
- final value = await associationMembershipListNotifier
- .updateAssociationMembership(
- associationMembership.copyWith(name: name.text),
- );
- if (value) {
- associationMembershipNotifier.setAssociationMembership(
- associationMembership.copyWith(
- name: name.text,
- managerGroupId: groupIdController.text,
- ),
- );
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.updatedAssociationMembership,
- );
- } else {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.updatingError,
- );
- }
- });
- },
- child: const Text(
- AdminTextConstants.edit,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Color.fromARGB(255, 255, 255, 255),
- ),
- ),
- ),
- ],
- ),
- ),
- ],
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_member_editable_card.dart b/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_member_editable_card.dart
deleted file mode 100644
index 1552f951e6..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_detail_page/association_membership_member_editable_card.dart
+++ /dev/null
@@ -1,112 +0,0 @@
-import 'package:auto_size_text/auto_size_text.dart';
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/user_association_membership.dart';
-import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
-import 'package:titan/admin/providers/user_association_membership_provider.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/phonebook/ui/pages/admin_page/delete_button.dart';
-import 'package:titan/phonebook/ui/pages/admin_page/edition_button.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/phonebook/tools/constants.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class MemberEditableCard extends HookConsumerWidget {
- const MemberEditableCard({super.key, required this.associationMembership});
-
- final UserAssociationMembership associationMembership;
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final associationMembershipMemberListNotifier = ref.watch(
- associationMembershipMembersProvider.notifier,
- );
- final userAssociationMembershipNotifier = ref.watch(
- userAssociationMembershipProvider.notifier,
- );
-
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- return Container(
- padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
- margin: const EdgeInsets.symmetric(vertical: 5),
- decoration: BoxDecoration(
- border: Border.all(),
- borderRadius: const BorderRadius.all(Radius.circular(20)),
- ),
- child: Row(
- children: [
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- AutoSizeText(
- (associationMembership.user.nickname ??
- "${associationMembership.user.firstname} ${associationMembership.user.name}"),
- style: const TextStyle(fontWeight: FontWeight.bold),
- minFontSize: 10,
- maxFontSize: 15,
- ),
- const SizedBox(height: 3),
- associationMembership.user.nickname != null
- ? AutoSizeText(
- "${associationMembership.user.firstname} ${associationMembership.user.name}",
- minFontSize: 10,
- maxFontSize: 15,
- )
- : const SizedBox(),
- ],
- ),
- ),
- Expanded(
- child: Column(
- children: [
- Text(associationMembership.startDate.toString().split(" ")[0]),
- Text(associationMembership.endDate.toString().split(" ")[0]),
- ],
- ),
- ),
- EditionButton(
- deactivated: false,
- onEdition: () async {
- userAssociationMembershipNotifier.setUserAssociationMembership(
- associationMembership,
- );
- QR.to(
- AdminRouter.root +
- AdminRouter.associationMemberships +
- AdminRouter.detailAssociationMembership +
- AdminRouter.addEditMember,
- );
- },
- ),
- const SizedBox(width: 10),
- DeleteButton(
- deactivated: false,
- deletion: true,
- onDelete: () async {
- await tokenExpireWrapper(ref, () async {
- final result = await associationMembershipMemberListNotifier
- .deleteMember(associationMembership);
- if (result) {
- displayToastWithContext(
- TypeMsg.msg,
- PhonebookTextConstants.deletedMember,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- PhonebookTextConstants.deletingError,
- );
- }
- });
- },
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_detail_page/research_bar.dart b/lib/admin/ui/pages/memberships/association_membership_detail_page/research_bar.dart
deleted file mode 100644
index 783cb553fb..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_detail_page/research_bar.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/research_filter_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/tools/constants.dart';
-
-class ResearchBar extends HookConsumerWidget {
- const ResearchBar({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final focusNode = useFocusNode();
- final editingController = useTextEditingController();
- final filterNotifier = ref.watch(filterProvider.notifier);
-
- return TextField(
- onChanged: (value) {
- filterNotifier.setFilter(value);
- },
- focusNode: focusNode,
- controller: editingController,
- cursorColor: Color(0xFF1D1D1D),
- decoration: const InputDecoration(
- isDense: true,
- suffixIcon: Icon(Icons.search, color: Color(0xFF1D1D1D), size: 30),
- label: Text(
- AdminTextConstants.research,
- style: TextStyle(color: Color(0xFF1D1D1D)),
- ),
- focusedBorder: UnderlineInputBorder(
- borderSide: BorderSide(color: ColorConstants.gradient1),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_detail_page/search_filters.dart b/lib/admin/ui/pages/memberships/association_membership_detail_page/search_filters.dart
deleted file mode 100644
index 694d366265..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_detail_page/search_filters.dart
+++ /dev/null
@@ -1,193 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
-import 'package:titan/admin/providers/association_membership_provider.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/tools/ui/layouts/add_edit_button_layout.dart';
-import 'package:titan/tools/ui/widgets/date_entry.dart';
-
-class SearchFilters extends HookConsumerWidget {
- const SearchFilters({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final associationMembershipMemberListNotifier = ref.watch(
- associationMembershipMembersProvider.notifier,
- );
- final associationMembership = ref.watch(associationMembershipProvider);
- final startMinimal = useTextEditingController(text: "");
- final startMaximal = useTextEditingController(
- text: processDate(DateTime.now()),
- );
- final endMinimal = useTextEditingController(
- text: processDate(DateTime.now()),
- );
- final endMaximal = useTextEditingController(text: "");
-
- return Column(
- children: [
- Row(
- children: [
- SizedBox(
- width: MediaQuery.of(context).size.width * 0.4,
- child: Column(
- children: [
- Text(
- AdminTextConstants.startDate,
- style: const TextStyle(fontSize: 18),
- ),
- DateEntry(
- label: AdminTextConstants.startDateMinimal,
- controller: startMinimal,
- onTap: () => getOnlyDayDate(
- context,
- startMinimal,
- firstDate: DateTime(2019),
- lastDate: DateTime(DateTime.now().year + 7),
- ),
- ),
- const SizedBox(height: 20),
- DateEntry(
- label: AdminTextConstants.startDateMaximal,
- controller: startMaximal,
- onTap: () => getOnlyDayDate(
- context,
- startMaximal,
- firstDate: DateTime(2019),
- lastDate: DateTime(DateTime.now().year + 7),
- ),
- ),
- ],
- ),
- ),
- const Spacer(),
- SizedBox(
- width: MediaQuery.of(context).size.width * 0.4,
- child: Column(
- children: [
- Text(
- AdminTextConstants.endDate,
- style: const TextStyle(fontSize: 18),
- ),
- DateEntry(
- label: AdminTextConstants.endDateMinimal,
- controller: endMinimal,
- onTap: () => getOnlyDayDate(
- context,
- endMinimal,
- firstDate: DateTime(2019),
- lastDate: DateTime(DateTime.now().year + 7),
- ),
- ),
- const SizedBox(height: 20),
- DateEntry(
- label: AdminTextConstants.endDateMaximal,
- controller: endMaximal,
- onTap: () => getOnlyDayDate(
- context,
- endMaximal,
- firstDate: DateTime(2019),
- lastDate: DateTime(DateTime.now().year + 7),
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- const SizedBox(height: 20),
- Row(
- children: [
- const Spacer(flex: 2),
- SizedBox(
- width: MediaQuery.of(context).size.width * 0.3,
- child: WaitingButton(
- onTap: () async {
- await tokenExpireWrapper(ref, () async {
- await associationMembershipMemberListNotifier
- .loadAssociationMembershipMembers(
- associationMembership.id,
- minimalStartDate: startMinimal.text.isNotEmpty
- ? DateTime.parse(
- processDateBack(startMinimal.text),
- )
- : null,
- minimalEndDate: endMinimal.text.isNotEmpty
- ? DateTime.parse(processDateBack(endMinimal.text))
- : null,
- maximalStartDate: startMaximal.text.isNotEmpty
- ? DateTime.parse(
- processDateBack(startMaximal.text),
- )
- : null,
- maximalEndDate: endMaximal.text.isNotEmpty
- ? DateTime.parse(processDateBack(endMaximal.text))
- : null,
- );
- });
- },
- builder: (child) => AddEditButtonLayout(
- colors: const [
- ColorConstants.gradient1,
- ColorConstants.gradient2,
- ],
- child: child,
- ),
- child: Text(
- AdminTextConstants.validateFilters,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Color.fromARGB(255, 255, 255, 255),
- ),
- textAlign: TextAlign.center,
- ),
- ),
- ),
- const Spacer(),
- SizedBox(
- width: MediaQuery.of(context).size.width * 0.3,
- child: WaitingButton(
- onTap: () async {
- startMaximal.clear();
- startMinimal.clear();
- endMaximal.clear();
- endMinimal.clear();
- await tokenExpireWrapper(ref, () async {
- await associationMembershipMemberListNotifier
- .loadAssociationMembershipMembers(
- associationMembership.id,
- );
- });
- },
- builder: (child) => AddEditButtonLayout(
- colors: const [
- ColorConstants.gradient1,
- ColorConstants.gradient2,
- ],
- child: child,
- ),
- child: Text(
- AdminTextConstants.clearFilters,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: Color.fromARGB(255, 255, 255, 255),
- ),
- textAlign: TextAlign.center,
- ),
- ),
- ),
- const Spacer(flex: 2),
- ],
- ),
- SizedBox(height: 10),
- ],
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_button.dart b/lib/admin/ui/pages/memberships/association_membership_page/association_membership_button.dart
deleted file mode 100644
index 9fd58fa128..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_button.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:flutter/material.dart';
-
-class AssociationMembershipButton extends StatelessWidget {
- final Widget child;
- final Color gradient1;
- final Color gradient2;
- const AssociationMembershipButton({
- super.key,
- required this.child,
- required this.gradient1,
- required this.gradient2,
- });
-
- @override
- Widget build(BuildContext context) {
- return Container(
- padding: const EdgeInsets.all(10),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [gradient1, gradient2],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: gradient2.withValues(alpha: 0.3),
- spreadRadius: 2,
- blurRadius: 4,
- offset: const Offset(2, 3),
- ),
- ],
- borderRadius: BorderRadius.circular(10),
- ),
- child: Center(child: child),
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_creation_dialog.dart b/lib/admin/ui/pages/memberships/association_membership_page/association_membership_creation_dialog.dart
deleted file mode 100644
index 9e0559b996..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_creation_dialog.dart
+++ /dev/null
@@ -1,149 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:titan/admin/class/simple_group.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/tools/constants.dart';
-
-class MembershipCreationDialogBox extends StatelessWidget {
- static const Color titleColor = ColorConstants.gradient1;
- static const Color descriptionColor = Colors.black;
- static const Color yesColor = ColorConstants.gradient2;
- static const Color noColor = ColorConstants.background2;
-
- final Function() onYes;
- final Function()? onNo;
- final TextEditingController nameController;
- final TextEditingController groupIdController;
- final List groups;
-
- static const double _padding = 20;
- static const double _avatarRadius = 45;
-
- static const Color background = Color(0xfffafafa);
- const MembershipCreationDialogBox({
- super.key,
- required this.nameController,
- required this.groupIdController,
- required this.onYes,
- required this.groups,
- this.onNo,
- });
-
- @override
- Widget build(BuildContext context) {
- groups.sort(
- (SimpleGroup a, SimpleGroup b) =>
- a.name.toLowerCase().compareTo(b.name.toLowerCase()),
- );
- groupIdController.text = groups.first.id;
- return Dialog(
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(
- MembershipCreationDialogBox._padding,
- ),
- ),
- elevation: 0,
- backgroundColor: Colors.transparent,
- child: Stack(
- children: [
- Container(
- padding: const EdgeInsets.all(MembershipCreationDialogBox._padding),
- margin: const EdgeInsets.only(
- top: MembershipCreationDialogBox._avatarRadius,
- ),
- decoration: BoxDecoration(
- shape: BoxShape.rectangle,
- color: MembershipCreationDialogBox.background,
- borderRadius: BorderRadius.circular(
- MembershipCreationDialogBox._padding,
- ),
- boxShadow: [
- BoxShadow(
- color: Colors.grey.shade700,
- offset: const Offset(0, 5),
- blurRadius: 5,
- ),
- ],
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- AdminTextConstants.createAssociationMembership,
- style: const TextStyle(
- fontSize: 25,
- fontWeight: FontWeight.w800,
- color: titleColor,
- ),
- ),
- const SizedBox(height: 15),
- TextField(
- controller: nameController,
- decoration: const InputDecoration(
- hintText: AdminTextConstants.associationMembershipName,
- ),
- ),
- const SizedBox(height: 20),
- DropdownButtonFormField(
- value: groupIdController.text,
- onChanged: (String? newValue) {
- groupIdController.text = newValue!;
- },
- items: groups
- .map(
- (SimpleGroup group) => DropdownMenuItem(
- value: group.id,
- child: Text(group.name),
- ),
- )
- .toList(),
- decoration: const InputDecoration(
- hintText: AdminTextConstants.group,
- ),
- ),
- const SizedBox(height: 20),
- Align(
- alignment: Alignment.bottomCenter,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- TextButton(
- onPressed: () async {
- Navigator.of(context).pop();
- await onYes();
- },
- child: const Text(
- "Créer",
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: noColor,
- ),
- ),
- ),
- TextButton(
- onPressed: () async {
- if (onNo == null) {
- Navigator.of(context).pop();
- }
- onNo?.call();
- },
- child: const Text(
- "Annuler",
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w600,
- color: yesColor,
- ),
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_page.dart b/lib/admin/ui/pages/memberships/association_membership_page/association_membership_page.dart
deleted file mode 100644
index 8ae53d5400..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_page.dart
+++ /dev/null
@@ -1,201 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/association_membership_simple.dart';
-import 'package:titan/admin/providers/all_groups_list_provider.dart';
-import 'package:titan/admin/providers/association_membership_list_provider.dart';
-import 'package:titan/admin/providers/association_membership_members_list_provider.dart';
-import 'package:titan/admin/providers/association_membership_provider.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/components/item_card_ui.dart';
-import 'package:titan/admin/tools/constants.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_page/association_membership_creation_dialog.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_page/association_membership_ui.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/ui/layouts/refresher.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class AssociationMembershipsPage extends HookConsumerWidget {
- const AssociationMembershipsPage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final associationsMemberships = ref.watch(
- allAssociationMembershipListProvider,
- );
- final associationMembershipsNotifier = ref.watch(
- allAssociationMembershipListProvider.notifier,
- );
- final associationMembershipNotifier = ref.watch(
- associationMembershipProvider.notifier,
- );
- final associationMembershipMembersNotifier = ref.watch(
- associationMembershipMembersProvider.notifier,
- );
- final groups = ref.watch(allGroupList);
-
- final nameController = TextEditingController();
- final groupIdController = TextEditingController();
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- return AdminTemplate(
- child: Refresher(
- onRefresh: () async {
- await associationMembershipsNotifier.loadAssociationMemberships();
- },
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: Column(
- children: [
- const SizedBox(height: 20),
- const Align(
- alignment: Alignment.centerLeft,
- child: Text(
- AdminTextConstants.associationsMemberships,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
- ),
- ),
- ),
- const SizedBox(height: 30),
- AsyncChild(
- value: associationsMemberships,
- builder: (context, g) {
- g.sort(
- (a, b) =>
- a.name.toLowerCase().compareTo(b.name.toLowerCase()),
- );
- return Column(
- children: [
- Column(
- children: [
- GestureDetector(
- onTap: () async {
- await showDialog(
- context: context,
- builder: (context) {
- return MembershipCreationDialogBox(
- groups: groups,
- nameController: nameController,
- groupIdController: groupIdController,
- onYes: () async {
- tokenExpireWrapper(ref, () async {
- final value =
- await associationMembershipsNotifier
- .createAssociationMembership(
- AssociationMembership.empty()
- .copyWith(
- managerGroupId:
- groupIdController
- .text,
- name:
- nameController.text,
- ),
- );
- if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants
- .createdAssociationMembership,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.creationError,
- );
- }
- });
- },
- );
- },
- );
- },
- child: ItemCardUi(
- children: [
- const Spacer(),
- HeroIcon(
- HeroIcons.plus,
- color: Colors.grey.shade700,
- size: 40,
- ),
- const Spacer(),
- ],
- ),
- ),
- ...g.map(
- (associationMembership) => AssociationMembershipUi(
- associationMembership: associationMembership,
- onEdit: () {
- associationMembershipMembersNotifier
- .loadAssociationMembershipMembers(
- associationMembership.id,
- );
- associationMembershipNotifier
- .setAssociationMembership(
- associationMembership,
- );
- QR.to(
- AdminRouter.root +
- AdminRouter.associationMemberships +
- AdminRouter.detailAssociationMembership,
- );
- },
- onDelete: () async {
- await showDialog(
- context: context,
- builder: (context) {
- return CustomDialogBox(
- title: AdminTextConstants.deleting,
- descriptions: AdminTextConstants
- .deleteAssociationMembership,
- onYes: () async {
- tokenExpireWrapper(ref, () async {
- final value =
- await associationMembershipsNotifier
- .deleteAssociationMembership(
- associationMembership,
- );
- if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants
- .deletedAssociationMembership,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.deletingError,
- );
- }
- });
- },
- );
- },
- );
- },
- ),
- ),
- const SizedBox(height: 20),
- ],
- ),
- ],
- );
- },
- loaderColor: ColorConstants.gradient1,
- ),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_ui.dart b/lib/admin/ui/pages/memberships/association_membership_page/association_membership_ui.dart
deleted file mode 100644
index 694a576747..0000000000
--- a/lib/admin/ui/pages/memberships/association_membership_page/association_membership_ui.dart
+++ /dev/null
@@ -1,62 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/class/association_membership_simple.dart';
-import 'package:titan/admin/ui/components/item_card_ui.dart';
-import 'package:titan/admin/ui/pages/memberships/association_membership_page/association_membership_button.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-
-class AssociationMembershipUi extends HookConsumerWidget {
- final AssociationMembership associationMembership;
- final void Function() onEdit;
- final Future Function() onDelete;
- const AssociationMembershipUi({
- super.key,
- required this.associationMembership,
- required this.onEdit,
- required this.onDelete,
- });
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- return ItemCardUi(
- children: [
- const SizedBox(width: 10),
- Expanded(
- child: Text(
- associationMembership.name,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- const SizedBox(width: 10),
- Row(
- children: [
- GestureDetector(
- onTap: onEdit,
- child: AssociationMembershipButton(
- gradient1: Colors.grey.shade800,
- gradient2: Colors.grey.shade900,
- child: const HeroIcon(HeroIcons.eye, color: Colors.white),
- ),
- ),
- const SizedBox(width: 10),
- WaitingButton(
- onTap: onDelete,
- builder: (child) => AssociationMembershipButton(
- gradient1: ColorConstants.gradient1,
- gradient2: ColorConstants.gradient2,
- child: child,
- ),
- child: const HeroIcon(HeroIcons.xMark, color: Colors.white),
- ),
- ],
- ),
- ],
- );
- }
-}
diff --git a/lib/admin/ui/pages/structure_page/add_edit_structure_page/add_edit_structure_page.dart b/lib/admin/ui/pages/structure_page/add_edit_structure_page/add_edit_structure_page.dart
new file mode 100644
index 0000000000..5ec47ef0e1
--- /dev/null
+++ b/lib/admin/ui/pages/structure_page/add_edit_structure_page/add_edit_structure_page.dart
@@ -0,0 +1,330 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/ui/pages/structure_page/add_edit_structure_page/user_search_modal.dart';
+import 'package:titan/admin/class/association_membership_simple.dart';
+import 'package:titan/admin/providers/association_membership_list_provider.dart';
+import 'package:titan/admin/providers/structure_manager_provider.dart';
+import 'package:titan/admin/providers/structure_provider.dart';
+import 'package:titan/paiement/class/structure.dart';
+import 'package:titan/paiement/providers/structure_list_provider.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
+import 'package:titan/tools/ui/layouts/item_chip.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+import 'package:titan/tools/ui/styleguide/text_entry.dart';
+import 'package:titan/user/class/simple_users.dart';
+import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
+
+class AddEditStructurePage extends HookConsumerWidget {
+ const AddEditStructurePage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final key = GlobalKey();
+ final structure = ref.watch(structureProvider);
+ final structureManager = ref.watch(structureManagerProvider);
+ final structureManagerNotifier = ref.watch(
+ structureManagerProvider.notifier,
+ );
+ final structureListNotifier = ref.watch(structureListProvider.notifier);
+ final isEdit = structure.id != '';
+ final name = useTextEditingController(text: isEdit ? structure.name : null);
+ final shortId = useTextEditingController(
+ text: isEdit ? structure.shortId : null,
+ );
+ final siegeAddressStreet = useTextEditingController(
+ text: isEdit ? structure.siegeAddressStreet : null,
+ );
+ final siegeAddressCity = useTextEditingController(
+ text: isEdit ? structure.siegeAddressCity : null,
+ );
+ final siegeAddressZipcode = useTextEditingController(
+ text: isEdit ? structure.siegeAddressZipcode : null,
+ );
+ final siegeAddressCountry = useTextEditingController(
+ text: isEdit ? structure.siegeAddressCountry : null,
+ );
+ final siret = useTextEditingController(
+ text: isEdit ? structure.siret : null,
+ );
+ final iban = useTextEditingController(text: isEdit ? structure.iban : null);
+ final bic = useTextEditingController(text: isEdit ? structure.bic : null);
+ final allAssociationMembershipList = ref.watch(
+ allAssociationMembershipListProvider,
+ );
+ final currentMembership = useState(
+ isEdit ? structure.associationMembership : AssociationMembership.empty(),
+ );
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return AdminTemplate(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
+ child: SingleChildScrollView(
+ physics: const AlwaysScrollableScrollPhysics(
+ parent: BouncingScrollPhysics(),
+ ),
+ child: Form(
+ key: key,
+ child: Column(
+ children: [
+ Text(
+ isEdit
+ ? localizeWithContext.adminEditStructure
+ : localizeWithContext.adminAddStructure,
+ style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ controller: name,
+ label: localizeWithContext.adminName,
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ controller: shortId,
+ label: localizeWithContext.adminShortId,
+ validator: (value) {
+ if (value.isNotEmpty && value.length != 3) {
+ return localizeWithContext.adminShortIdError;
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: 30),
+ Text(
+ localizeWithContext.adminSiegeAddress,
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 10),
+ TextEntry(
+ controller: siegeAddressStreet,
+ label: localizeWithContext.adminStreet,
+ ),
+ const SizedBox(height: 20),
+ Row(
+ children: [
+ Expanded(
+ flex: 2,
+ child: TextEntry(
+ controller: siegeAddressCity,
+ label: localizeWithContext.adminCity,
+ ),
+ ),
+ const SizedBox(width: 20),
+ Expanded(
+ child: TextEntry(
+ controller: siegeAddressZipcode,
+ label: localizeWithContext.adminZipcode,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ controller: siegeAddressCountry,
+ label: localizeWithContext.adminCountry,
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ controller: siret,
+ label: localizeWithContext.adminSiret,
+ validator: (value) {
+ if (value.isNotEmpty &&
+ value.replaceAll(" ", "").length != 14) {
+ return localizeWithContext.adminSiretError;
+ }
+ return null;
+ },
+ canBeEmpty: true,
+ ),
+ const SizedBox(height: 20),
+ Text(
+ localizeWithContext.adminBankDetails,
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ controller: iban,
+ label: localizeWithContext.adminIban,
+ validator: (value) {
+ if (value.isNotEmpty &&
+ value.replaceAll(" ", "").length != 27) {
+ return localizeWithContext.adminIbanError;
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: 20),
+ TextEntry(
+ controller: bic,
+ label: localizeWithContext.adminBic,
+ validator: (value) {
+ if (value.isNotEmpty &&
+ value.replaceAll(" ", "").length != 11) {
+ return localizeWithContext.adminBicError;
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: 20),
+ AsyncChild(
+ value: allAssociationMembershipList,
+ builder: (context, allAssociationMembershipList) {
+ return HorizontalListView.builder(
+ height: 40,
+ items: [
+ ...allAssociationMembershipList,
+ AssociationMembership.empty(),
+ ],
+ itemBuilder: (context, associationMembership, index) {
+ final selected =
+ currentMembership.value.id ==
+ associationMembership.id;
+ return ItemChip(
+ selected: selected,
+ onTap: () async {
+ currentMembership.value = associationMembership;
+ },
+ child: Text(
+ associationMembership.name.toUpperCase(),
+ style: TextStyle(
+ color: selected ? Colors.white : Colors.black,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ );
+ },
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ isEdit
+ ? Column(
+ children: [
+ Text(
+ localizeWithContext.adminManager,
+ style: TextStyle(
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 10),
+ Text(
+ structureManager.getName(),
+ style: TextStyle(
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 10),
+ ],
+ )
+ : ListItem(
+ title: structureManager.id.isNotEmpty
+ ? structureManager.getName()
+ : localizeWithContext.adminSelectManager,
+ subtitle: structureManager.getName(),
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: UserSearchModal(),
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ Button(
+ onPressed: () async {
+ if (key.currentState == null) {
+ return;
+ }
+ if (structureManager.id.isEmpty && !isEdit) {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext.adminNoManager,
+ );
+ return;
+ }
+ if (key.currentState!.validate()) {
+ await tokenExpireWrapper(ref, () async {
+ final editedStructureMsg = isEdit
+ ? localizeWithContext.adminEditedStructure
+ : localizeWithContext.adminAddedStructure;
+ final addedStructureErrorMsg = AppLocalizations.of(
+ context,
+ )!.adminAddingError;
+ final value = isEdit
+ ? await structureListNotifier.updateStructure(
+ Structure(
+ id: structure.id,
+ shortId: shortId.text,
+ name: name.text,
+ siegeAddressStreet: siegeAddressStreet.text,
+ siegeAddressCity: siegeAddressCity.text,
+ siegeAddressZipcode: siegeAddressZipcode.text,
+ siegeAddressCountry: siegeAddressCountry.text,
+ siret: siret.text,
+ iban: iban.text,
+ bic: bic.text,
+ associationMembership:
+ currentMembership.value,
+ managerUser: structureManager,
+ ),
+ )
+ : await structureListNotifier.createStructure(
+ Structure(
+ id: '',
+ shortId: shortId.text,
+ name: name.text,
+ siegeAddressStreet: siegeAddressStreet.text,
+ siegeAddressCity: siegeAddressCity.text,
+ siegeAddressZipcode: siegeAddressZipcode.text,
+ siegeAddressCountry: siegeAddressCountry.text,
+ siret: siret.text,
+ iban: iban.text,
+ bic: bic.text,
+ associationMembership:
+ currentMembership.value,
+ managerUser: structureManager,
+ ),
+ );
+ if (value) {
+ QR.back();
+ structureManagerNotifier.setUser(SimpleUser.empty());
+ displayToastWithContext(
+ TypeMsg.msg,
+ editedStructureMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ addedStructureErrorMsg,
+ );
+ }
+ });
+ }
+ },
+ text: isEdit
+ ? localizeWithContext.adminEdit
+ : localizeWithContext.adminAdd,
+ ),
+ SizedBox(height: 80),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/structure_page/add_edit_structure_page/search_result.dart b/lib/admin/ui/pages/structure_page/add_edit_structure_page/search_result.dart
new file mode 100644
index 0000000000..9d30ca97f6
--- /dev/null
+++ b/lib/admin/ui/pages/structure_page/add_edit_structure_page/search_result.dart
@@ -0,0 +1,55 @@
+import 'package:flutter/material.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/structure_manager_provider.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/user/providers/user_list_provider.dart';
+
+class SearchResult extends HookConsumerWidget {
+ final TextEditingController queryController;
+ const SearchResult({super.key, required this.queryController});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final users = ref.watch(userList);
+ final usersNotifier = ref.watch(userList.notifier);
+ final structureManagerNotifier = ref.watch(
+ structureManagerProvider.notifier,
+ );
+
+ return AsyncChild(
+ value: users,
+ builder: (context, usersData) {
+ return Column(
+ children: usersData
+ .map(
+ (user) => Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Text(
+ user.getName(),
+ style: const TextStyle(fontSize: 15),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ GestureDetector(
+ onTap: () {
+ structureManagerNotifier.setUser(user);
+ usersNotifier.clear();
+ Navigator.of(context).pop();
+ },
+ child: const HeroIcon(HeroIcons.plus),
+ ),
+ ],
+ ),
+ ),
+ )
+ .toList(),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/structure_page/add_edit_structure_page/user_search_modal.dart b/lib/admin/ui/pages/structure_page/add_edit_structure_page/user_search_modal.dart
new file mode 100644
index 0000000000..b6c35f61c5
--- /dev/null
+++ b/lib/admin/ui/pages/structure_page/add_edit_structure_page/user_search_modal.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/ui/pages/structure_page/add_edit_structure_page/search_result.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/searchbar.dart';
+import 'package:titan/user/providers/user_list_provider.dart';
+
+class UserSearchModal extends HookConsumerWidget {
+ const UserSearchModal({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final usersNotifier = ref.watch(userList.notifier);
+ final textController = useTextEditingController();
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ return BottomModalTemplate(
+ title: localizeWithContext.adminSelectManager,
+ type: BottomModalType.main,
+ child: Column(
+ children: [
+ CustomSearchBar(
+ autofocus: true,
+ onSearch: (value) => tokenExpireWrapper(ref, () async {
+ if (value.isNotEmpty) {
+ await usersNotifier.filterUsers(value);
+ textController.text = value;
+ } else {
+ usersNotifier.clear();
+ textController.clear();
+ }
+ }),
+ ),
+ const SizedBox(height: 10),
+ ConstrainedBox(
+ constraints: const BoxConstraints(maxHeight: 280),
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ child: SearchResult(queryController: textController),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/structure_page/structure_button.dart b/lib/admin/ui/pages/structure_page/structure_button.dart
deleted file mode 100644
index 9c29cae36c..0000000000
--- a/lib/admin/ui/pages/structure_page/structure_button.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:flutter/material.dart';
-
-class GroupButton extends StatelessWidget {
- final Widget child;
- final Color gradient1;
- final Color gradient2;
- const GroupButton({
- super.key,
- required this.child,
- required this.gradient1,
- required this.gradient2,
- });
-
- @override
- Widget build(BuildContext context) {
- return Container(
- padding: const EdgeInsets.all(10),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [gradient1, gradient2],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: gradient2.withValues(alpha: 0.3),
- spreadRadius: 2,
- blurRadius: 4,
- offset: const Offset(2, 3),
- ),
- ],
- borderRadius: BorderRadius.circular(10),
- ),
- child: Center(child: child),
- );
- }
-}
diff --git a/lib/admin/ui/pages/structure_page/structure_page.dart b/lib/admin/ui/pages/structure_page/structure_page.dart
index 82b20900f7..3f302e0a6e 100644
--- a/lib/admin/ui/pages/structure_page/structure_page.dart
+++ b/lib/admin/ui/pages/structure_page/structure_page.dart
@@ -1,17 +1,19 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/admin.dart';
+import 'package:titan/admin/router.dart';
import 'package:titan/admin/providers/structure_manager_provider.dart';
import 'package:titan/admin/providers/structure_provider.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/admin/ui/admin.dart';
-import 'package:titan/admin/ui/components/item_card_ui.dart';
-import 'package:titan/admin/ui/pages/structure_page/structure_ui.dart';
-import 'package:titan/admin/tools/constants.dart';
import 'package:titan/paiement/class/structure.dart';
+import 'package:titan/paiement/providers/bank_account_holder_provider.dart';
import 'package:titan/paiement/providers/structure_list_provider.dart';
import 'package:titan/tools/constants.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
@@ -19,12 +21,18 @@ import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/user/class/simple_users.dart';
import 'package:titan/user/providers/user_list_provider.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:tuple/tuple.dart';
class StructurePage extends HookConsumerWidget {
const StructurePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final bankAccountHolder = ref.watch(bankAccountHolderProvider);
+ final bankAccountHolderNotifier = ref.watch(
+ bankAccountHolderProvider.notifier,
+ );
final structures = ref.watch(structureListProvider);
final structuresNotifier = ref.watch(structureListProvider.notifier);
final structureNotifier = ref.watch(structureProvider.notifier);
@@ -37,31 +45,53 @@ class StructurePage extends HookConsumerWidget {
displayToast(context, type, msg);
}
+ final localizeWithContext = AppLocalizations.of(context)!;
+
return AdminTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await structuresNotifier.getStructures();
},
child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
- const Align(
- alignment: Alignment.centerLeft,
- child: Text(
- AdminTextConstants.structures,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
+ Row(
+ children: [
+ Text(
+ localizeWithContext.adminStructures,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: ColorConstants.title,
+ ),
+ ),
+ const Spacer(),
+ CustomIconButton(
+ icon: HeroIcon(
+ HeroIcons.plus,
+ color: Colors.white,
+ size: 30,
+ ),
+ onPressed: () {
+ structureNotifier.setStructure(Structure.empty());
+ structureManagerNotifier.setUser(SimpleUser.empty());
+ QR.to(
+ AdminRouter.root +
+ AdminRouter.structures +
+ AdminRouter.addEditStructure,
+ );
+ },
),
- ),
+ ],
),
- const SizedBox(height: 30),
- AsyncChild(
- value: structures,
- builder: (context, structures) {
+ const SizedBox(height: 10),
+ Async2Children(
+ values: Tuple2(structures, bankAccountHolder),
+ builder: (context, structures, bankAccountHolder) {
structures.sort(
(a, b) =>
a.name.toLowerCase().compareTo(b.name.toLowerCase()),
@@ -70,73 +100,136 @@ class StructurePage extends HookConsumerWidget {
children: [
Column(
children: [
- GestureDetector(
- onTap: () {
- structureNotifier.setStructure(Structure.empty());
- structureManagerNotifier.setUser(
- SimpleUser.empty(),
- );
- QR.to(
- AdminRouter.root +
- AdminRouter.structures +
- AdminRouter.addEditStructure,
- );
- },
- child: ItemCardUi(
- children: [
- const Spacer(),
- HeroIcon(
- HeroIcons.plus,
- color: Colors.grey.shade700,
- size: 40,
- ),
- const Spacer(),
- ],
+ Text(
+ bankAccountHolder.id == ""
+ ? localizeWithContext
+ .adminUndefinedBankAccountHolder
+ : localizeWithContext.adminBankAccountHolder(
+ bankAccountHolder.name,
+ ),
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
),
),
+ const SizedBox(height: 10),
...structures.map(
- (structure) => StructureUi(
- group: structure,
- onEdit: () {
- structureNotifier.setStructure(structure);
- structureManagerNotifier.setUser(
- structure.managerUser,
- );
- QR.to(
- AdminRouter.root +
- AdminRouter.structures +
- AdminRouter.addEditStructure,
- );
- },
- onDelete: () async {
- await showDialog(
- context: context,
- builder: (context) {
- return CustomDialogBox(
- title: AdminTextConstants.deleting,
- descriptions:
- AdminTextConstants.deleteGroup,
- onYes: () async {
- tokenExpireWrapper(ref, () async {
- final value = await structuresNotifier
- .deleteStructure(structure);
- if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdminTextConstants.deletedGroup,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdminTextConstants.deletingError,
- );
- }
- });
- },
- );
- },
- );
- },
+ (structure) => Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 5.0,
+ ),
+ child: ListItem(
+ title: structure.name,
+ subtitle: structure.managerUser.getName(),
+ onTap: () async {
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: structure.name,
+ child: Column(
+ children: [
+ Button(
+ text: localizeWithContext.adminEdit,
+ onPressed: () {
+ structureNotifier.setStructure(
+ structure,
+ );
+ structureManagerNotifier.setUser(
+ structure.managerUser,
+ );
+
+ QR.to(
+ AdminRouter.root +
+ AdminRouter.structures +
+ AdminRouter
+ .addEditStructure,
+ );
+ Navigator.of(context).pop();
+ },
+ ),
+ const SizedBox(height: 10),
+ Button(
+ text: localizeWithContext
+ .adminDefineAsBankAccountHolder,
+ onPressed: () async {
+ final value =
+ await bankAccountHolderNotifier
+ .updateBankAccountHolder(
+ structure,
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext
+ .adminBankAccountHolderModified,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ localizeWithContext
+ .adminError,
+ );
+ }
+ },
+ ),
+ const SizedBox(height: 10),
+ Button(
+ type: ButtonType.danger,
+ text:
+ localizeWithContext.adminDelete,
+ onPressed: () async {
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return CustomDialogBox(
+ title: AppLocalizations.of(
+ context,
+ )!.adminDeleting,
+ descriptions:
+ AppLocalizations.of(
+ context,
+ )!.adminDeleteGroup,
+ onYes: () async {
+ final deletedGroupMsg =
+ localizeWithContext
+ .adminDeletedGroup;
+ final deletingErrorMsg =
+ localizeWithContext
+ .adminDeletingError;
+ tokenExpireWrapper(ref, () async {
+ final value =
+ await structuresNotifier
+ .deleteStructure(
+ structure,
+ );
+ if (value) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ deletedGroupMsg,
+ );
+ } else {
+ displayToastWithContext(
+ TypeMsg.error,
+ deletingErrorMsg,
+ );
+ }
+ });
+ Navigator.of(
+ context,
+ ).pop();
+ },
+ );
+ },
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
),
),
const SizedBox(height: 20),
@@ -145,7 +238,6 @@ class StructurePage extends HookConsumerWidget {
],
);
},
- loaderColor: ColorConstants.gradient1,
),
],
),
diff --git a/lib/admin/ui/pages/structure_page/structure_ui.dart b/lib/admin/ui/pages/structure_page/structure_ui.dart
deleted file mode 100644
index cd75918502..0000000000
--- a/lib/admin/ui/pages/structure_page/structure_ui.dart
+++ /dev/null
@@ -1,62 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/ui/components/item_card_ui.dart';
-import 'package:titan/admin/ui/pages/structure_page/structure_button.dart';
-import 'package:titan/paiement/class/structure.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-
-class StructureUi extends HookConsumerWidget {
- final Structure group;
- final void Function() onEdit;
- final Future Function() onDelete;
- const StructureUi({
- super.key,
- required this.group,
- required this.onEdit,
- required this.onDelete,
- });
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- return ItemCardUi(
- children: [
- const SizedBox(width: 10),
- Expanded(
- child: Text(
- group.name,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- const SizedBox(width: 10),
- Row(
- children: [
- GestureDetector(
- onTap: onEdit,
- child: GroupButton(
- gradient1: Colors.grey.shade800,
- gradient2: Colors.grey.shade900,
- child: const HeroIcon(HeroIcons.eye, color: Colors.white),
- ),
- ),
- const SizedBox(width: 10),
- WaitingButton(
- onTap: onDelete,
- builder: (child) => GroupButton(
- gradient1: ColorConstants.gradient1,
- gradient2: ColorConstants.gradient2,
- child: child,
- ),
- child: const HeroIcon(HeroIcons.xMark, color: Colors.white),
- ),
- ],
- ),
- ],
- );
- }
-}
diff --git a/lib/admin/ui/pages/users_management_page/add_user_modal.dart b/lib/admin/ui/pages/users_management_page/add_user_modal.dart
new file mode 100644
index 0000000000..7f6173bce3
--- /dev/null
+++ b/lib/admin/ui/pages/users_management_page/add_user_modal.dart
@@ -0,0 +1,176 @@
+import 'dart:io';
+
+import 'package:file_picker/file_picker.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/class/simple_group.dart';
+import 'package:titan/admin/providers/all_groups_list_provider.dart';
+import 'package:titan/admin/providers/user_invitation_provider.dart';
+import 'package:titan/admin/tools/functions.dart' as admin_utils;
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/paiement/ui/pages/main_page/account_card/device_dialog_box.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/functions.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+import 'package:titan/tools/ui/styleguide/list_item.dart';
+
+class AddUsersModalContent extends HookConsumerWidget {
+ const AddUsersModalContent({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final groups = ref.watch(allGroupList);
+ final selectedFileName = useState(null);
+ final mailList = useState>([]);
+ final chosenGroup = useState(null);
+ final userInvitationNotifier = ref.watch(userInvitationProvider.notifier);
+
+ void displayToastWithContext(TypeMsg type, String msg) {
+ displayToast(context, type, msg);
+ }
+
+ final localizeWithContext = AppLocalizations.of(context)!;
+
+ final navigatorWithContext = Navigator.of(context);
+
+ return BottomModalTemplate(
+ title: localizeWithContext.adminInviteUsers,
+ child: Column(
+ children: [
+ Text(
+ localizeWithContext.adminImportUsersDescription,
+ style: TextStyle(fontSize: 16),
+ ),
+ const SizedBox(height: 20),
+ Button.secondary(
+ text: selectedFileName.value ?? localizeWithContext.adminImportList,
+ onPressed: () async {
+ final result = await FilePicker.platform.pickFiles(
+ type: FileType.custom,
+ allowedExtensions: ['csv'],
+ );
+ if (result != null && result.files.isNotEmpty) {
+ final file = result.files.first;
+
+ selectedFileName.value = file.name;
+
+ if (file.path != null) {
+ String fileContent = '';
+ if (kIsWeb) {
+ fileContent = file.bytes != null
+ ? String.fromCharCodes(file.bytes!)
+ : '';
+ } else {
+ fileContent = await File(file.path!).readAsString();
+ }
+ mailList.value = admin_utils.parseCsvContent(fileContent);
+ }
+ }
+ },
+ ),
+ const SizedBox(height: 20),
+ Text(
+ localizeWithContext.adminInviteUsersCounter(mailList.value.length),
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
+ ),
+ const SizedBox(height: 30),
+ ListItem(
+ title: chosenGroup.value?.name ?? localizeWithContext.adminNoGroup,
+ onTap: () async {
+ FocusScope.of(context).unfocus();
+ final ctx = context;
+ await Future.delayed(Duration(milliseconds: 150));
+ if (!ctx.mounted) return;
+
+ await showCustomBottomModal(
+ context: ctx,
+ ref: ref,
+ modal: BottomModalTemplate(
+ title: localizeWithContext.adminChooseGroup,
+ child: ConstrainedBox(
+ constraints: BoxConstraints(maxHeight: 600),
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ ...groups.map(
+ (e) => ListItem(
+ title: e.name,
+ onTap: () {
+ chosenGroup.value = e;
+ Navigator.of(ctx).pop();
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ const SizedBox(height: 30),
+ WaitingButton(
+ builder: (child) => Container(
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
+ decoration: BoxDecoration(
+ color: ColorConstants.tertiary,
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: ColorConstants.onTertiary),
+ ),
+ child: Center(child: child),
+ ),
+ onTap: () async {
+ if (selectedFileName.value == null) return;
+ await tokenExpireWrapper(ref, () async {
+ final value = await userInvitationNotifier.createUsers(
+ mailList.value,
+ chosenGroup.value?.id,
+ );
+ if (value.isEmpty) {
+ displayToastWithContext(
+ TypeMsg.msg,
+ localizeWithContext.adminInvitedUsers,
+ );
+ navigatorWithContext.pop();
+ } else {
+ if (!context.mounted) return;
+ await showDialog(
+ context: context,
+ builder: (BuildContext context) => DeviceDialogBox(
+ descriptions: value.map((e) => '- $e').join('\n'),
+ title: AppLocalizations.of(context)!.adminEmailFailed,
+ buttonText: "Compris",
+ onClick: () async {
+ Navigator.of(context).pop();
+ },
+ ),
+ );
+ }
+ });
+ },
+ child: Text(
+ localizeWithContext.adminInvite,
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Color.fromARGB(
+ 255,
+ 255,
+ 255,
+ 255,
+ ).withAlpha(selectedFileName.value == null ? 100 : 255),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/admin/ui/pages/users_management_page/users_management_page.dart b/lib/admin/ui/pages/users_management_page/users_management_page.dart
new file mode 100644
index 0000000000..f02a7ee3cc
--- /dev/null
+++ b/lib/admin/ui/pages/users_management_page/users_management_page.dart
@@ -0,0 +1,62 @@
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/ui/pages/users_management_page/add_user_modal.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/tools/ui/styleguide/bottom_modal_template.dart';
+import 'package:titan/tools/ui/styleguide/button.dart';
+
+class UsersManagementPage extends HookConsumerWidget {
+ const UsersManagementPage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final localizeWithContext = AppLocalizations.of(context)!;
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Button(
+ text: localizeWithContext.adminAdd,
+ onPressed: () async {
+ Navigator.pop(context);
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: AddUsersModalContent(),
+ );
+ },
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext.adminDelete,
+ onPressed: () async {
+ Navigator.pop(context);
+ await showCustomBottomModal(
+ context: context,
+ ref: ref,
+ modal: BottomModalTemplate(
+ type: BottomModalType.danger,
+ title: localizeWithContext.adminDeleteUsers,
+ child: Column(
+ children: [
+ Button(
+ text: localizeWithContext.adminImportList,
+ onPressed: () {},
+ type: ButtonType.onDanger,
+ ),
+ const SizedBox(height: 20),
+ Button(
+ text: localizeWithContext.adminDelete,
+ onPressed: () {},
+ disabled: true,
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ type: ButtonType.danger,
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/advert/class/advert.dart b/lib/advert/class/advert.dart
index 1fe15b5355..b8f781ef3e 100644
--- a/lib/advert/class/advert.dart
+++ b/lib/advert/class/advert.dart
@@ -1,4 +1,3 @@
-import 'package:titan/advert/class/announcer.dart';
import 'package:titan/tools/functions.dart';
class Advert {
@@ -6,16 +5,18 @@ class Advert {
late final String title;
late final String content;
late final DateTime date;
- late final Announcer announcer;
- late final List tags;
+ late final String associationId;
+ late final bool postToFeed;
+ late final bool notification;
Advert({
required this.id,
required this.title,
required this.content,
required this.date,
- required this.announcer,
- required this.tags,
+ required this.associationId,
+ required this.postToFeed,
+ required this.notification,
});
Advert.fromJson(Map json) {
@@ -23,8 +24,9 @@ class Advert {
title = json["title"];
content = json["content"];
date = processDateFromAPI(json["date"]);
- announcer = Announcer.fromJson(json["advertiser"]);
- tags = json["tags"].split(', ');
+ associationId = json["advertiser_id"];
+ postToFeed = json["post_to_feed"] ?? false;
+ notification = json["notification"] ?? true;
}
Map toJson() {
@@ -33,8 +35,9 @@ class Advert {
data["title"] = title;
data["content"] = content;
data["date"] = processDateToAPI(date);
- data["advertiser_id"] = announcer.id;
- data["tags"] = tags.join(', ');
+ data["advertiser_id"] = associationId;
+ data["post_to_feed"] = postToFeed;
+ data["notification"] = notification;
return data;
}
@@ -43,16 +46,18 @@ class Advert {
String? title,
String? content,
DateTime? date,
- Announcer? announcer,
- List? tags,
+ String? associationId,
+ bool? postToFeed,
+ bool? notification,
}) {
return Advert(
id: id ?? this.id,
title: title ?? this.title,
content: content ?? this.content,
date: date ?? this.date,
- announcer: announcer ?? this.announcer,
- tags: tags ?? this.tags,
+ associationId: associationId ?? this.associationId,
+ postToFeed: postToFeed ?? this.postToFeed,
+ notification: notification ?? this.notification,
);
}
@@ -62,13 +67,14 @@ class Advert {
title: "",
content: "",
date: DateTime.now(),
- announcer: Announcer.empty(),
- tags: [],
+ associationId: "",
+ postToFeed: false,
+ notification: true,
);
}
@override
String toString() {
- return 'Advert{id: $id, title: $title, content: $content, date: $date, announcer: $announcer, tags: $tags}';
+ return 'Advert{id: $id, title: $title, content: $content, date: $date, association_id: $associationId, postToFeed: $postToFeed, notification: $notification}';
}
}
diff --git a/lib/advert/class/announcer.dart b/lib/advert/class/announcer.dart
deleted file mode 100644
index e76d7c7c5e..0000000000
--- a/lib/advert/class/announcer.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-class Announcer {
- Announcer({
- required this.name,
- required this.groupManagerId,
- required this.id,
- });
- late final String name;
- late final String groupManagerId;
- late final String id;
-
- Announcer.fromJson(Map json) {
- name = json['name'];
- groupManagerId = json['group_manager_id'];
- id = json['id'];
- }
-
- Map toJson() {
- final data = {};
- data['name'] = name;
- data['group_manager_id'] = groupManagerId;
- data['id'] = id;
- return data;
- }
-
- Announcer copyWith({String? name, String? groupManagerId, String? id}) {
- return Announcer(
- name: name ?? this.name,
- groupManagerId: groupManagerId ?? this.groupManagerId,
- id: id ?? this.id,
- );
- }
-
- Announcer.empty() {
- name = "";
- groupManagerId = "";
- id = "";
- }
-
- @override
- String toString() {
- return 'Announcer(name: $name, groupManagerId: $groupManagerId, id: $id)';
- }
-}
diff --git a/lib/advert/class/tag.dart b/lib/advert/class/tag.dart
deleted file mode 100644
index 6b19c33d12..0000000000
--- a/lib/advert/class/tag.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-class Tag {
- late final String id;
- late final String name;
-
- Tag({required this.id, required this.name});
-
- Tag.fromJson(Map json) {
- id = json["id"];
- name = json["name"];
- }
-
- Map toJson() {
- final data = {};
- data["id"] = id;
- data["name"] = name;
- return data;
- }
-
- Tag copyWith({String? id, String? name}) {
- return Tag(id: id ?? this.id, name: name ?? this.name);
- }
-
- static Tag empty() {
- return Tag(id: "", name: "");
- }
-
- @override
- String toString() {
- return 'Tag{id: $id, name: $name}';
- }
-}
diff --git a/lib/advert/providers/all_announcer_list_provider.dart b/lib/advert/providers/all_announcer_list_provider.dart
deleted file mode 100644
index 95d0fb63a6..0000000000
--- a/lib/advert/providers/all_announcer_list_provider.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:titan/advert/class/announcer.dart';
-import 'package:titan/advert/providers/announcer_list_provider.dart';
-
-final allAnnouncerList = Provider>((ref) {
- final announcersProvider = ref.watch(announcerListProvider);
- return announcersProvider.maybeWhen(
- data: (announcers) => announcers,
- orElse: () => [],
- );
-});
diff --git a/lib/advert/providers/announcer_list_provider.dart b/lib/advert/providers/announcer_list_provider.dart
deleted file mode 100644
index 52a223c9df..0000000000
--- a/lib/advert/providers/announcer_list_provider.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/auth/providers/openid_provider.dart';
-import 'package:titan/advert/class/announcer.dart';
-import 'package:titan/advert/repositories/announcer_repository.dart';
-import 'package:titan/tools/providers/list_notifier.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-
-class AnnouncerListNotifier extends ListNotifier {
- final AnnouncerRepository _announcerRepository = AnnouncerRepository();
- AnnouncerListNotifier({required String token})
- : super(const AsyncValue.loading()) {
- _announcerRepository.setToken(token);
- }
-
- Future>> loadAllAnnouncerList() async {
- return await loadList(_announcerRepository.getAllAnnouncer);
- }
-
- Future>> loadMyAnnouncerList() async {
- return await loadList(_announcerRepository.getMyAnnouncer);
- }
-
- Future addAnnouncer(Announcer announcer) async {
- return await add(_announcerRepository.createAnnouncer, announcer);
- }
-
- Future updateAnnouncer(Announcer announcer) async {
- return await update(
- _announcerRepository.updateAnnouncer,
- (announcers, announcer) =>
- announcers
- ..[announcers.indexWhere((i) => i.id == announcer.id)] = announcer,
- announcer,
- );
- }
-
- Future deleteAnnouncer(Announcer announcer) async {
- return await delete(
- _announcerRepository.deleteAnnouncer,
- (adverts, advert) => adverts..removeWhere((i) => i.id == advert.id),
- announcer.id,
- announcer,
- );
- }
-}
-
-final announcerListProvider =
- StateNotifierProvider>>((
- ref,
- ) {
- final token = ref.watch(tokenProvider);
- AnnouncerListNotifier announcerListNotifier = AnnouncerListNotifier(
- token: token,
- );
- tokenExpireWrapperAuth(ref, () async {
- await announcerListNotifier.loadAllAnnouncerList();
- });
- return announcerListNotifier;
- });
-
-final userAnnouncerListProvider =
- StateNotifierProvider>>((
- ref,
- ) {
- final token = ref.watch(tokenProvider);
- AnnouncerListNotifier announcerListNotifier = AnnouncerListNotifier(
- token: token,
- );
- tokenExpireWrapperAuth(ref, () async {
- await announcerListNotifier.loadMyAnnouncerList();
- });
- return announcerListNotifier;
- });
diff --git a/lib/advert/providers/announcer_provider.dart b/lib/advert/providers/announcer_provider.dart
deleted file mode 100644
index df895c9ca5..0000000000
--- a/lib/advert/providers/announcer_provider.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:titan/advert/class/announcer.dart';
-
-final announcerProvider =
- StateNotifierProvider>((ref) {
- return AnnouncerNotifier();
- });
-
-class AnnouncerNotifier extends StateNotifier> {
- AnnouncerNotifier() : super([]);
-
- void addAnnouncer(Announcer i) {
- state.add(i);
- state = state.sublist(0);
- }
-
- void removeAnnouncer(Announcer i) {
- state = state.where((element) => element.id != i.id).toList();
- }
-
- void clearAnnouncer() {
- state = [];
- }
-}
diff --git a/lib/advert/providers/is_advert_admin_provider.dart b/lib/advert/providers/is_advert_admin_provider.dart
deleted file mode 100644
index 3dec527d3a..0000000000
--- a/lib/advert/providers/is_advert_admin_provider.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/advert/providers/announcer_list_provider.dart';
-
-final isAdvertAdminProvider = StateProvider((ref) {
- final me = ref.watch(userAnnouncerListProvider);
- return me.maybeWhen(data: (data) => data.isNotEmpty, orElse: () => false);
-});
diff --git a/lib/advert/providers/selected_association_provider.dart b/lib/advert/providers/selected_association_provider.dart
new file mode 100644
index 0000000000..5a84bf07ad
--- /dev/null
+++ b/lib/advert/providers/selected_association_provider.dart
@@ -0,0 +1,24 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/admin/class/assocation.dart';
+
+final selectedAssociationProvider =
+ StateNotifierProvider>((ref) {
+ return AssociationNotifier();
+ });
+
+class AssociationNotifier extends StateNotifier> {
+ AssociationNotifier() : super([]);
+
+ void addAssociation(Association i) {
+ state.add(i);
+ state = state.sublist(0);
+ }
+
+ void removeAssociation(Association i) {
+ state = state.where((element) => element.id != i.id).toList();
+ }
+
+ void clearAssociation() {
+ state = [];
+ }
+}
diff --git a/lib/advert/providers/user_association_list_provider.dart b/lib/advert/providers/user_association_list_provider.dart
new file mode 100644
index 0000000000..a078544d04
--- /dev/null
+++ b/lib/advert/providers/user_association_list_provider.dart
@@ -0,0 +1,28 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/advert/class/advert.dart';
+import 'package:titan/advert/repositories/advert_repository.dart';
+import 'package:titan/tools/providers/list_notifier.dart';
+import 'package:titan/tools/token_expire_wrapper.dart';
+
+class AdvertListNotifier extends ListNotifier {
+ AdvertRepository repository = AdvertRepository();
+ AdvertListNotifier({required String token})
+ : super(const AsyncValue.loading()) {
+ repository.setToken(token);
+ }
+
+ Future>> loadUserAssmicationList() async {
+ return await loadList(repository.getAllAdvert);
+ }
+}
+
+final advertListProvider =
+ StateNotifierProvider>>((ref) {
+ final token = ref.watch(tokenProvider);
+ AdvertListNotifier notifier = AdvertListNotifier(token: token);
+ tokenExpireWrapperAuth(ref, () async {
+ await notifier.loadUserAssmicationList();
+ });
+ return notifier;
+ });
diff --git a/lib/advert/repositories/advert_repository.dart b/lib/advert/repositories/advert_repository.dart
index befbc7da72..26b452d9e9 100644
--- a/lib/advert/repositories/advert_repository.dart
+++ b/lib/advert/repositories/advert_repository.dart
@@ -12,6 +12,12 @@ class AdvertRepository extends Repository {
)).map((e) => Advert.fromJson(e)).toList();
}
+ Future> getAllAdminAdvert() async {
+ return (await getList(
+ suffix: 'adverts/admin',
+ )).map((e) => Advert.fromJson(e)).toList();
+ }
+
Future getAdvert(String id) async {
return Advert.fromJson(await getOne(id));
}
diff --git a/lib/advert/repositories/announcer_repository.dart b/lib/advert/repositories/announcer_repository.dart
deleted file mode 100644
index 40b611a357..0000000000
--- a/lib/advert/repositories/announcer_repository.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-import 'package:titan/advert/class/announcer.dart';
-import 'package:titan/tools/repository/repository.dart';
-
-class AnnouncerRepository extends Repository {
- @override
- // ignore: overridden_fields
- final ext = "advert/";
-
- Future> getAllAnnouncer() async {
- return List.from(
- (await getList(suffix: "advertisers")).map((x) => Announcer.fromJson(x)),
- );
- }
-
- Future> getMyAnnouncer() async {
- return List.from(
- (await getList(
- suffix: "me/advertisers",
- )).map((x) => Announcer.fromJson(x)),
- );
- }
-
- Future getAnnouncer(String id) async {
- return Announcer.fromJson(await getOne("advertisers/$id"));
- }
-
- Future createAnnouncer(Announcer announcer) async {
- return Announcer.fromJson(
- await create(announcer.toJson(), suffix: "advertisers"),
- );
- }
-
- Future updateAnnouncer(Announcer announcer) async {
- return await update(announcer.toJson(), "advertisers/${announcer.id}");
- }
-
- Future deleteAnnouncer(String announcerId) async {
- return await delete("advertisers/$announcerId");
- }
-}
diff --git a/lib/advert/repositories/tag_repository.dart b/lib/advert/repositories/tag_repository.dart
deleted file mode 100644
index 01f25b0d3e..0000000000
--- a/lib/advert/repositories/tag_repository.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-import 'package:titan/advert/class/tag.dart';
-import 'package:titan/tools/repository/repository.dart';
-
-class TagRepository extends Repository {
- @override
- // ignore: overridden_fields
- final ext = "advert/tag/";
-
- Future> getAllTag() async {
- return (await getList()).map((e) => Tag.fromJson(e)).toList();
- }
-
- Future getTag(String id) async {
- return Tag.fromJson(await getOne(id));
- }
-
- Future addTag(Tag tag) async {
- return Tag.fromJson(await create(tag.toJson()));
- }
-
- Future deleteTag(String id) async {
- return await delete("/$id");
- }
-}
diff --git a/lib/advert/router.dart b/lib/advert/router.dart
index 769f6ee5ee..48b95dde3b 100644
--- a/lib/advert/router.dart
+++ b/lib/advert/router.dart
@@ -1,19 +1,15 @@
-import 'package:either_dart/either.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:titan/admin/providers/is_admin_provider.dart';
-import 'package:titan/advert/providers/is_advert_admin_provider.dart';
+
import 'package:titan/advert/ui/pages/admin_page/admin_page.dart'
deferred as admin_page;
-import 'package:titan/advert/ui/pages/detail_page/detail.dart'
- deferred as detail_page;
import 'package:titan/advert/ui/pages/form_page/add_edit_advert_page.dart'
deferred as add_edit_advert_page;
-import 'package:titan/advert/ui/pages/form_page/add_rem_announcer_page.dart'
- deferred as add_rem_announcer_page;
import 'package:titan/advert/ui/pages/main_page/main_page.dart'
deferred as main_page;
-import 'package:titan/drawer/class/module.dart';
+import 'package:titan/feed/providers/is_user_a_member_of_an_association.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
import 'package:titan/tools/middlewares/admin_middleware.dart';
import 'package:titan/tools/middlewares/authenticated_middleware.dart';
import 'package:titan/tools/middlewares/deferred_middleware.dart';
@@ -24,13 +20,11 @@ class AdvertRouter {
static const String root = '/advert';
static const String admin = '/admin';
static const String addEditAdvert = '/add_edit_advert';
- static const String addRemAnnouncer = '/add_remove_announcer';
- static const String detail = '/detail';
static final Module module = Module(
- name: "Annonce",
- icon: const Left(HeroIcons.megaphone),
+ getName: (context) => AppLocalizations.of(context)!.moduleAdvert,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleAdvertDescription,
root: AdvertRouter.root,
- selected: false,
);
AdvertRouter(this.ref);
@@ -42,12 +36,16 @@ class AdvertRouter {
AuthenticatedMiddleware(ref),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
children: [
QRoute(
path: admin,
builder: () => admin_page.AdvertAdminPage(),
middleware: [
- AdminMiddleware(ref, isAdvertAdminProvider),
+ AdminMiddleware(ref, isUserAMemberOfAnAssociationProvider),
DeferredLoadingMiddleware(admin_page.loadLibrary),
],
children: [
@@ -60,19 +58,6 @@ class AdvertRouter {
),
],
),
- QRoute(
- path: detail,
- builder: () => detail_page.AdvertDetailPage(),
- middleware: [DeferredLoadingMiddleware(detail_page.loadLibrary)],
- ),
- QRoute(
- path: addRemAnnouncer,
- builder: () => add_rem_announcer_page.AddRemAnnouncerPage(),
- middleware: [
- AdminMiddleware(ref, isAdminProvider),
- DeferredLoadingMiddleware(add_rem_announcer_page.loadLibrary),
- ],
- ),
],
);
}
diff --git a/lib/advert/tools/constants.dart b/lib/advert/tools/constants.dart
index d1f8eb8100..43a97337d8 100644
--- a/lib/advert/tools/constants.dart
+++ b/lib/advert/tools/constants.dart
@@ -1,51 +1,5 @@
import 'dart:ui';
-class AdvertTextConstants {
- static const String add = 'Ajouter';
- static const String addedAdvert = 'Annonce publiée';
- static const String addedAnnouncer = 'Annonceur ajouté';
- static const String addingError = 'Erreur lors de l\'ajout';
- static const String admin = 'Admin';
- static const String advert = 'Annonce';
- static const String choosingAnnouncer = 'Veuillez choisir un annonceur';
- static const String choosingPoster = 'Veuillez choisir une image';
- static const String content = 'Contenu';
- static const String deleteAdvert = 'Supprimer l\'annonce ?';
- static const String deleteAnnouncer = 'Supprimer l\'annonceur ?';
- static const String deleting = 'Suppression';
- static const String edit = 'Modifier';
- static const String editedAdvert = 'Annonce modifiée';
- static const String editingError = 'Erreur lors de la modification';
- static const String groupAdvert = 'Groupe';
- static const String incorrectOrMissingFields =
- 'Champs incorrects ou manquants';
- static const String invalidNumber = 'Veuillez entrer un nombre';
- static const String management = 'Gestion';
- static const String modifyAnnouncingGroup = 'Modifier un groupe d\'annonce';
- static const String noMoreAnnouncer = 'Aucun annonceur n\'est disponible';
- static const String noValue = 'Veuillez entrer une valeur';
- static const String positiveNumber = 'Veuillez entrer un nombre positif';
- static const String removedAnnouncer = 'Annonceur supprimé';
- static const String removingError = 'Erreur lors de la suppression';
- static const String tags = 'Tags';
- static const String title = 'Titre';
-
- static const List months = [
- 'Janv.',
- 'Févr.',
- 'Mars',
- 'Avr.',
- 'Mai',
- 'Juin',
- 'Juill.',
- 'Août',
- 'Sept.',
- 'Oct.',
- 'Nov.',
- 'Déc.',
- ];
-}
-
class AdvertColorConstants {
static const Color redGradient1 = Color(0xFF9E131F);
static const Color redGradient2 = Color(0xFF590512);
diff --git a/lib/advert/tools/functions.dart b/lib/advert/tools/functions.dart
index fd26da6d2e..e07b1d9741 100644
--- a/lib/advert/tools/functions.dart
+++ b/lib/advert/tools/functions.dart
@@ -1,4 +1,5 @@
-import 'dart:ui';
+import 'package:flutter/material.dart';
+import 'package:titan/l10n/app_localizations.dart';
Color invert(Color color) {
return Color.from(
@@ -18,3 +19,21 @@ Color generateColor(String uuid) {
double luminance = color.computeLuminance();
return luminance < 0.5 ? color : invert(color);
}
+
+List getLocalizedMonths(BuildContext context) {
+ final l10n = AppLocalizations.of(context)!;
+ return [
+ l10n.advertMonthJan,
+ l10n.advertMonthFeb,
+ l10n.advertMonthMar,
+ l10n.advertMonthApr,
+ l10n.advertMonthMay,
+ l10n.advertMonthJun,
+ l10n.advertMonthJul,
+ l10n.advertMonthAug,
+ l10n.advertMonthSep,
+ l10n.advertMonthOct,
+ l10n.advertMonthNov,
+ l10n.advertMonthDec,
+ ];
+}
diff --git a/lib/advert/ui/components/advert_card.dart b/lib/advert/ui/components/advert_card.dart
deleted file mode 100644
index dfbce33dfc..0000000000
--- a/lib/advert/ui/components/advert_card.dart
+++ /dev/null
@@ -1,318 +0,0 @@
-import 'package:auto_size_text/auto_size_text.dart';
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:intl/intl.dart';
-import 'package:titan/advert/class/advert.dart';
-import 'package:titan/advert/providers/advert_poster_provider.dart';
-import 'package:titan/advert/providers/advert_posters_provider.dart';
-import 'package:titan/advert/tools/constants.dart';
-import 'package:titan/cinema/tools/functions.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
-import 'package:titan/tools/ui/builders/auto_loader_child.dart';
-import 'package:titan/tools/ui/widgets/text_with_hyper_link.dart';
-
-class AdvertCard extends HookConsumerWidget {
- final VoidCallback onTap;
- final Advert advert;
-
- const AdvertCard({super.key, required this.onTap, required this.advert});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- double width = 300;
- double height = 300;
- double imageHeight = 175;
- double maxHeight = MediaQuery.of(context).size.height - 344;
- final posters = ref.watch(
- advertPostersProvider.select((advertPosters) => advertPosters[advert.id]),
- );
- final advertPostersNotifier = ref.watch(advertPostersProvider.notifier);
- final posterNotifier = ref.watch(advertPosterProvider.notifier);
- final isWebFormat = ref.watch(isWebFormatProvider);
- return GestureDetector(
- onTap: () {
- if (!isWebFormat) {
- onTap();
- }
- },
- child: Container(
- margin: const EdgeInsets.all(10),
- padding: EdgeInsets.all(isWebFormat ? 50 : 0),
- child: isWebFormat
- ? Container(
- height: maxHeight,
- width: double.infinity,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(30),
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- ClipRRect(
- borderRadius: BorderRadius.circular(30),
- child: AspectRatio(
- aspectRatio: 2 / 3,
- child: AutoLoaderChild(
- group: posters,
- notifier: advertPostersNotifier,
- mapKey: advert.id,
- loader: (advertId) =>
- posterNotifier.getAdvertPoster(advertId),
- loadingBuilder: (context) =>
- HeroIcon(HeroIcons.photo, size: width),
- dataBuilder: (context, value) => Image(
- image: value.first.image,
- fit: BoxFit.cover, // use this
- ),
- ),
- ),
- ),
- const SizedBox(width: 50),
- Expanded(
- child: Column(
- children: [
- AutoSizeText(
- advert.title,
- maxLines: 2,
- overflow: TextOverflow.ellipsis,
- textAlign: TextAlign.center,
- style: const TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- const SizedBox(height: 10),
- AutoSizeText(
- formatDate(advert.date),
- maxLines: 1,
- textAlign: TextAlign.center,
- style: const TextStyle(fontSize: 16),
- ),
- const SizedBox(height: 10),
- Expanded(
- child: SingleChildScrollView(
- child: TextWithHyperLink(
- advert.content,
- textAlign: TextAlign.center,
- style: const TextStyle(fontSize: 16),
- ),
- ),
- ),
- ],
- ),
- ),
- const SizedBox(width: 50),
- ],
- ),
- )
- : Container(
- margin: const EdgeInsets.symmetric(vertical: 10),
- width: width,
- height: height,
- decoration: BoxDecoration(
- color: Colors.white,
- boxShadow: const [
- BoxShadow(
- blurRadius: 5,
- color: Color(0x33000000),
- offset: Offset(2, 2),
- spreadRadius: 3,
- ),
- ],
- borderRadius: BorderRadius.circular(20),
- ),
- child: Stack(
- children: [
- Column(
- children: [
- AutoLoaderChild(
- group: posters,
- notifier: advertPostersNotifier,
- mapKey: advert.id,
- loader: (advertId) =>
- posterNotifier.getAdvertPoster(advertId),
- loadingBuilder: (context) => Container(
- width: width,
- height: imageHeight,
- decoration: const BoxDecoration(
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(20),
- topRight: Radius.circular(20),
- ),
- ),
- child: HeroIcon(HeroIcons.photo, size: width),
- ),
- dataBuilder: (context, value) => Container(
- width: width,
- height: imageHeight,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(20),
- topRight: Radius.circular(20),
- ),
- image: DecorationImage(
- image: value.first.image,
- fit: BoxFit.cover,
- ),
- ),
- ),
- ),
- Container(
- padding: const EdgeInsets.only(
- top: 20,
- left: 10,
- right: 10,
- ),
- width: width,
- height: height - imageHeight,
- child: Column(
- children: [
- Column(
- children: [
- Container(
- width: width,
- margin: const EdgeInsets.only(bottom: 5),
- child: AutoSizeText(
- advert.title.trim(),
- textAlign: TextAlign.left,
- overflow: TextOverflow.ellipsis,
- maxLines: 1,
- minFontSize: 15,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 16,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ],
- ),
- TextWithHyperLink(
- advert.content.trim(),
- overflow: TextOverflow.ellipsis,
- textAlign: TextAlign.justify,
- maxLines: 3,
- minFontSize: 13,
- maxFontSize: 15,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 15,
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- Positioned(
- top: imageHeight - 40,
- left: 15,
- child: Container(
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.all(
- Radius.circular(8),
- ),
- boxShadow: [
- BoxShadow(
- blurRadius: 5,
- color: Colors.black.withValues(alpha: 0.3),
- offset: const Offset(2, 2),
- spreadRadius: 3,
- ),
- ],
- ),
- child: ClipRRect(
- borderRadius: const BorderRadius.all(
- Radius.circular(8),
- ),
- child: Container(
- color: Colors.white,
- height: 50,
- width: 50,
- padding: const EdgeInsets.symmetric(
- horizontal: 5,
- vertical: 5,
- ),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- AutoSizeText(
- DateFormat('dd').format(advert.date),
- textAlign: TextAlign.center,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 22,
- fontWeight: FontWeight.bold,
- height: 1.0,
- ),
- ),
- AutoSizeText(
- AdvertTextConstants.months[int.parse(
- DateFormat('MM').format(advert.date),
- ) -
- 1],
- textAlign: TextAlign.center,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 11,
- fontWeight: FontWeight.bold,
- height: 1.0,
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- Positioned(
- top: imageHeight - 20,
- right: 15,
- child: Container(
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.all(
- Radius.circular(8),
- ),
- boxShadow: [
- BoxShadow(
- blurRadius: 5,
- color: Colors.black.withValues(alpha: 0.3),
- offset: const Offset(2, 2),
- spreadRadius: 3,
- ),
- ],
- ),
- child: ClipRRect(
- borderRadius: const BorderRadius.all(
- Radius.circular(8),
- ),
- child: Container(
- color: Colors.white,
- height: 30,
- padding: const EdgeInsets.symmetric(
- vertical: 5,
- horizontal: 10,
- ),
- alignment: Alignment.center,
- child: AutoSizeText(
- advert.announcer.name,
- textAlign: TextAlign.center,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 14,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/advert/ui/components/announcer_bar.dart b/lib/advert/ui/components/announcer_bar.dart
deleted file mode 100644
index ce1a331382..0000000000
--- a/lib/advert/ui/components/announcer_bar.dart
+++ /dev/null
@@ -1,66 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/advert/providers/announcer_provider.dart';
-import 'package:titan/advert/providers/announcer_list_provider.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
-import 'package:titan/tools/ui/layouts/item_chip.dart';
-
-class AnnouncerBar extends HookConsumerWidget {
- final bool useUserAnnouncers;
- final bool multipleSelect;
- final bool isNotClickable;
- const AnnouncerBar({
- super.key,
- required this.multipleSelect,
- required this.useUserAnnouncers,
- this.isNotClickable = false,
- });
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final selected = ref.watch(announcerProvider);
- final selectedId = selected.map((e) => e.id).toList();
- final selectedNotifier = ref.read(announcerProvider.notifier);
- final announcerList = useUserAnnouncers
- ? ref.watch(userAnnouncerListProvider)
- : ref.watch(announcerListProvider);
- final darkerColor = (isNotClickable) ? Colors.grey[800] : Colors.black;
-
- return AsyncChild(
- value: announcerList,
- builder: (context, userAnnouncers) => HorizontalListView.builder(
- height: 40,
- items: userAnnouncers,
- itemBuilder: (context, e, i) => GestureDetector(
- onTap: () {
- if (isNotClickable) {
- return;
- }
- if (multipleSelect) {
- selectedId.contains(e.id)
- ? selectedNotifier.removeAnnouncer(e)
- : selectedNotifier.addAnnouncer(e);
- } else {
- bool contain = selectedId.contains(e.id);
- selectedNotifier.clearAnnouncer();
- if (!contain) {
- selectedNotifier.addAnnouncer(e);
- }
- }
- },
- child: ItemChip(
- selected: selectedId.contains(e.id),
- child: Text(
- e.name,
- style: TextStyle(
- color: selectedId.contains(e.id) ? Colors.white : darkerColor,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/advert/ui/components/association_bar.dart b/lib/advert/ui/components/association_bar.dart
new file mode 100644
index 0000000000..2d85a55942
--- /dev/null
+++ b/lib/advert/ui/components/association_bar.dart
@@ -0,0 +1,83 @@
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/assocation_list_provider.dart';
+import 'package:titan/admin/providers/my_association_list_provider.dart';
+import 'package:titan/advert/providers/selected_association_provider.dart';
+import 'package:titan/advert/ui/components/association_item.dart';
+import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
+
+class AssociationBar extends HookConsumerWidget {
+ final bool useUserAssociations;
+ final bool multipleSelect;
+ final bool isNotClickable;
+ const AssociationBar({
+ super.key,
+ required this.multipleSelect,
+ required this.useUserAssociations,
+ this.isNotClickable = false,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final selected = ref.watch(selectedAssociationProvider);
+ final selectedId = selected.map((e) => e.id).toList();
+ final selectedNotifier = ref.read(selectedAssociationProvider.notifier);
+ final associationList = useUserAssociations
+ ? ref.watch(asyncMyAssociationListProvider)
+ : ref.watch(associationListProvider);
+ return AsyncChild(
+ value: associationList,
+ builder: (context, userAssociations) {
+ return HorizontalListView.builder(
+ height: 66,
+ items: userAssociations,
+ itemBuilder: (context, e, i) {
+ final selected = selectedId.contains(e.id);
+ return AssociationItem(
+ onTap: () {
+ if (isNotClickable) {
+ return;
+ }
+ if (multipleSelect) {
+ selected
+ ? selectedNotifier.removeAssociation(e)
+ : selectedNotifier.addAssociation(e);
+ } else {
+ selectedNotifier.clearAssociation();
+ if (!selected) {
+ selectedNotifier.addAssociation(e);
+ }
+ }
+ },
+ associationId: e.id,
+ name: e.name,
+ avatarName: () {
+ try {
+ final name = e.name.trim();
+ if (name.length <= 3) {
+ return name.toUpperCase();
+ }
+ final parts = name
+ .split(RegExp(r"[ '\s]+"))
+ .where((s) => s.isNotEmpty)
+ .toList();
+
+ if (parts.length >= 2) {
+ return parts.take(2).map((s) => s[0].toUpperCase()).join();
+ }
+ return name.substring(0, 3).toUpperCase();
+ } catch (_) {
+ return (e.name.length >= 3)
+ ? e.name.substring(0, 3).toUpperCase()
+ : e.name.toUpperCase();
+ }
+ }(),
+ selected: selected,
+ );
+ },
+ );
+ },
+ );
+ }
+}
diff --git a/lib/advert/ui/components/association_item.dart b/lib/advert/ui/components/association_item.dart
new file mode 100644
index 0000000000..7167789e0d
--- /dev/null
+++ b/lib/advert/ui/components/association_item.dart
@@ -0,0 +1,107 @@
+import 'package:auto_size_text/auto_size_text.dart';
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/association_logo_provider.dart';
+import 'package:titan/admin/providers/associations_logo_map_provider.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/ui/builders/auto_loader_child.dart';
+
+class AssociationItem extends ConsumerWidget {
+ final String name, avatarName, associationId;
+ final bool selected;
+ final VoidCallback onTap;
+ const AssociationItem({
+ super.key,
+
+ required this.name,
+ required this.onTap,
+ required this.selected,
+ required this.avatarName,
+ required this.associationId,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final associationLogo = ref.watch(
+ associationLogoMapProvider.select((value) => value[associationId]),
+ );
+ final associationLogoMapNotifier = ref.watch(
+ associationLogoMapProvider.notifier,
+ );
+ final associationLogoNotifier = ref.watch(associationLogoProvider.notifier);
+ return GestureDetector(
+ onTap: onTap,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Center(
+ child: AutoLoaderChild(
+ group: associationLogo,
+ notifier: associationLogoMapNotifier,
+ mapKey: associationId,
+ loader: (associationId) =>
+ associationLogoNotifier.getAssociationLogo(associationId),
+ dataBuilder: (context, data) => Container(
+ width: 44,
+ height: 44,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: selected
+ ? Border.all(color: ColorConstants.tertiary, width: 3)
+ : null,
+ image: DecorationImage(
+ image: data.first.image,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ orElseBuilder: (context, stack) => Container(
+ width: 44,
+ height: 44,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: selected
+ ? Border.all(color: ColorConstants.tertiary, width: 3)
+ : null,
+ color: Colors.grey.shade100,
+ ),
+ child: Center(
+ child: Text(
+ avatarName,
+ style: TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.bold,
+ color: selected
+ ? ColorConstants.onTertiary
+ : ColorConstants.tertiary,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 4),
+ SizedBox(
+ width: 55,
+ child: AutoSizeText(
+ name,
+ textAlign: TextAlign.center,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 12,
+ color: selected
+ ? ColorConstants.onTertiary
+ : ColorConstants.tertiary,
+ fontWeight: selected ? FontWeight.bold : FontWeight.normal,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/advert/ui/components/special_action_button.dart b/lib/advert/ui/components/special_action_button.dart
new file mode 100644
index 0000000000..b490804672
--- /dev/null
+++ b/lib/advert/ui/components/special_action_button.dart
@@ -0,0 +1,54 @@
+import 'package:flutter/material.dart';
+import 'package:titan/tools/constants.dart';
+
+class SpecialActionButton extends StatelessWidget {
+ final String name;
+ final VoidCallback onTap;
+ final Widget icon;
+ const SpecialActionButton({
+ super.key,
+ required this.name,
+ required this.onTap,
+ required this.icon,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: onTap,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 5.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ width: 45,
+ height: 45,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(color: ColorConstants.onTertiary, width: 2),
+ color: ColorConstants.tertiary,
+ ),
+ child: Center(child: icon),
+ ),
+ const SizedBox(height: 4),
+ SizedBox(
+ width: 55,
+ child: Text(
+ name,
+ textAlign: TextAlign.center,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 12,
+ color: ColorConstants.onTertiary,
+ fontWeight: FontWeight.normal,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/advert/ui/components/tag_chip.dart b/lib/advert/ui/components/tag_chip.dart
deleted file mode 100644
index fba084f3c5..0000000000
--- a/lib/advert/ui/components/tag_chip.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-import 'dart:math';
-
-import 'package:flutter/material.dart';
-import 'package:titan/advert/tools/functions.dart';
-
-class TagChip extends StatelessWidget {
- final String tagName;
-
- const TagChip({super.key, required this.tagName});
-
- @override
- Widget build(BuildContext context) {
- Color bgColor = generateColor(tagName);
- Color borderColor = bgColor.computeLuminance() > 0.1
- ? bgColor
- : Colors.white;
- Color darkerBgColor = Color.from(
- alpha: bgColor.a,
- red: max(bgColor.r - 0.12, 0), // 0.12 = 30/255
- green: max(bgColor.g - 0.12, 0),
- blue: max(bgColor.b - 0.12, 0),
- );
-
- return Container(
- margin: const EdgeInsets.symmetric(horizontal: 5),
- padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
- height: 30,
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topLeft,
- end: Alignment.bottomLeft,
- colors: [bgColor, darkerBgColor],
- stops: const [0.7, 1.0],
- ),
- borderRadius: BorderRadius.circular(20),
- border: Border.all(color: borderColor),
- ),
- child: Align(
- alignment: Alignment.center,
- child: Text(
- tagName,
- textAlign: TextAlign.center,
- maxLines: 1,
- style: const TextStyle(color: Colors.white, fontSize: 13),
- ),
- ),
- );
- }
-}
diff --git a/lib/advert/ui/pages/admin_page/admin_advert_card.dart b/lib/advert/ui/pages/admin_page/admin_advert_card.dart
index 7397504045..4f4560a659 100644
--- a/lib/advert/ui/pages/admin_page/admin_advert_card.dart
+++ b/lib/advert/ui/pages/admin_page/admin_advert_card.dart
@@ -1,72 +1,86 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/my_association_list_provider.dart';
import 'package:titan/advert/class/advert.dart';
-import 'package:titan/advert/tools/constants.dart';
-import 'package:titan/advert/ui/components/advert_card.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/tools/ui/layouts/card_button.dart';
+import 'package:timeago/timeago.dart' as timeago;
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/ui/styleguide/icon_button.dart';
class AdminAdvertCard extends HookConsumerWidget {
- final VoidCallback onTap, onEdit;
+ final VoidCallback onEdit;
final Future Function() onDelete;
final Advert advert;
const AdminAdvertCard({
super.key,
required this.advert,
- required this.onTap,
required this.onEdit,
required this.onDelete,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final myAssociations = ref.watch(myAssociationListProvider);
+ final myAssociationIdList = myAssociations.map((e) => e.id).toList();
return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30),
- child: Stack(
- children: [
- AdvertCard(onTap: onTap, advert: advert),
- Positioned(
- top: 10,
- right: 15,
- child: Container(
- margin: const EdgeInsets.only(top: 30),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- GestureDetector(
- onTap: onEdit,
- child: CardButton(
- colors: [Colors.grey.shade100, Colors.grey.shade400],
- shadowColor: Colors.grey.shade300.withValues(alpha: 0.2),
- child: const HeroIcon(
- HeroIcons.pencil,
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Container(
+ margin: const EdgeInsets.all(10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ advert.title,
+ style: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
color: Colors.black,
),
),
- ),
- const SizedBox(width: 20),
- WaitingButton(
- onTap: onDelete,
- builder: (child) => CardButton(
- colors: const [
- AdvertColorConstants.redGradient1,
- AdvertColorConstants.redGradient2,
- ],
- shadowColor: AdvertColorConstants.redGradient2.withValues(
- alpha: 0.2,
+ Text(
+ _capitalizeFirst(
+ timeago.format(advert.date, locale: 'fr_short'),
+ ),
+ style: const TextStyle(
+ fontSize: 12,
+ color: ColorConstants.tertiary,
),
- child: child,
),
- child: const HeroIcon(HeroIcons.trash, color: Colors.white),
+ ],
+ ),
+ const Spacer(),
+ if (myAssociationIdList.contains(advert.associationId))
+ CustomIconButton.secondary(
+ onPressed: onEdit,
+ icon: const HeroIcon(
+ HeroIcons.pencil,
+ color: ColorConstants.tertiary,
+ ),
),
- ],
- ),
+ const SizedBox(width: 20),
+ CustomIconButton.danger(
+ onPressed: onDelete,
+ icon: const HeroIcon(
+ HeroIcons.trash,
+ color: ColorConstants.background,
+ ),
+ ),
+ ],
),
- ),
- ],
+ ],
+ ),
),
);
}
+
+ String _capitalizeFirst(String text) {
+ if (text.isEmpty) return text;
+ return text[0].toUpperCase() + text.substring(1);
+ }
}
diff --git a/lib/advert/ui/pages/admin_page/admin_page.dart b/lib/advert/ui/pages/admin_page/admin_page.dart
index 6092ee5bc9..31c746c1df 100644
--- a/lib/advert/ui/pages/admin_page/admin_page.dart
+++ b/lib/advert/ui/pages/admin_page/admin_page.dart
@@ -2,22 +2,25 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/is_admin_provider.dart';
+import 'package:titan/admin/providers/my_association_list_provider.dart';
import 'package:titan/advert/class/advert.dart';
import 'package:titan/advert/providers/advert_list_provider.dart';
import 'package:titan/advert/providers/advert_posters_provider.dart';
import 'package:titan/advert/providers/advert_provider.dart';
-import 'package:titan/advert/providers/announcer_list_provider.dart';
-import 'package:titan/advert/providers/announcer_provider.dart';
-import 'package:titan/advert/tools/constants.dart';
+import 'package:titan/advert/providers/selected_association_provider.dart';
+import 'package:titan/advert/ui/components/special_action_button.dart';
import 'package:titan/advert/ui/pages/admin_page/admin_advert_card.dart';
import 'package:titan/advert/ui/pages/advert.dart';
import 'package:titan/advert/router.dart';
-import 'package:titan/advert/ui/components/announcer_bar.dart';
+import 'package:titan/advert/ui/components/association_bar.dart';
+import 'package:titan/feed/providers/is_user_a_member_of_an_association.dart';
+import 'package:titan/tools/constants.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/layouts/card_layout.dart';
-import 'package:titan/tools/ui/layouts/column_refresher.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AdvertAdminPage extends HookConsumerWidget {
const AdvertAdminPage({super.key});
@@ -25,118 +28,157 @@ class AdvertAdminPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final advertNotifier = ref.watch(advertProvider.notifier);
+ final isAdmin = ref.watch(isAdminProvider);
final advertList = ref.watch(advertListProvider);
- final userAnnouncerListNotifier = ref.watch(
- userAnnouncerListProvider.notifier,
- );
- final userAnnouncerList = ref.watch(userAnnouncerListProvider);
final advertPostersNotifier = ref.watch(advertPostersProvider.notifier);
- final advertListNotifier = ref.watch(advertListProvider.notifier);
- final selectedAnnouncers = ref.watch(announcerProvider);
- final selectedAnnouncersNotifier = ref.read(announcerProvider.notifier);
+ final selectedAssociations = ref.watch(selectedAssociationProvider);
+ final selectedAssociationsNotifier = ref.read(
+ selectedAssociationProvider.notifier,
+ );
+ final myAssociationList = ref.watch(myAssociationListProvider);
+ final myAssociationListNotifier = ref.watch(
+ asyncMyAssociationListProvider.notifier,
+ );
+ final isAdvertAdmin = ref.watch(isUserAMemberOfAnAssociationProvider);
+
return AdvertTemplate(
- child: AsyncChild(
- value: advertList,
- builder: (context, advertData) => AsyncChild(
- value: userAnnouncerList,
- builder: (context, userAnnouncerData) {
- final userAnnouncerAdvert = advertData.where(
- (advert) => userAnnouncerData
- .where((element) => advert.announcer.id == element.id)
- .isNotEmpty,
- );
- final sortedUserAnnouncerAdverts = userAnnouncerAdvert
- .toList()
- .sortedBy((element) => element.date)
- .reversed;
- final filteredSortedUserAnnouncerAdverts =
- sortedUserAnnouncerAdverts
- .where(
- (advert) =>
- selectedAnnouncers
- .where((e) => advert.announcer.id == e.id)
- .isNotEmpty ||
- selectedAnnouncers.isEmpty,
- )
- .toList();
- return ColumnRefresher(
- onRefresh: () async {
- await advertListNotifier.loadAdverts();
- await userAnnouncerListNotifier.loadMyAnnouncerList();
- advertPostersNotifier.resetTData();
- },
- children: [
- const AnnouncerBar(
- useUserAnnouncers: true,
+ child: Column(
+ children: [
+ Row(
+ children: [
+ Expanded(
+ child: AssociationBar(
+ useUserAssociations: !isAdmin,
multipleSelect: true,
),
- GestureDetector(
+ ),
+ if (!isAdmin || isAdvertAdmin) ...[
+ SizedBox(width: 5),
+ Container(
+ width: 2,
+ height: 60,
+ color: ColorConstants.secondary,
+ ),
+ SizedBox(width: 5),
+ SpecialActionButton(
onTap: () {
advertNotifier.setAdvert(Advert.empty());
+ if (myAssociationList.length == 1 &&
+ selectedAssociations.isEmpty) {
+ selectedAssociationsNotifier.addAssociation(
+ myAssociationList[0],
+ );
+ }
QR.to(
AdvertRouter.root +
AdvertRouter.admin +
AdvertRouter.addEditAdvert,
);
},
- child: CardLayout(
- margin: const EdgeInsets.only(
- bottom: 10,
- top: 20,
- left: 30,
- right: 30,
- ),
- width: 300,
- height: 100,
- colors: [Colors.white, Colors.grey.shade100],
- shadowColor: Colors.grey.withValues(alpha: 0.2),
- child: Center(
- child: HeroIcon(
- HeroIcons.plus,
- size: 40,
- color: Colors.grey.shade500,
- ),
- ),
- ),
- ),
- ...filteredSortedUserAnnouncerAdverts.map(
- (advert) => AdminAdvertCard(
- onTap: () {
- advertNotifier.setAdvert(advert);
- QR.to(AdvertRouter.root + AdvertRouter.detail);
- },
- onEdit: () {
- QR.to(
- AdvertRouter.root +
- AdvertRouter.admin +
- AdvertRouter.addEditAdvert,
- );
- advertNotifier.setAdvert(advert);
- selectedAnnouncersNotifier.clearAnnouncer();
- selectedAnnouncersNotifier.addAnnouncer(advert.announcer);
- },
- onDelete: () async {
- await showDialog(
- context: context,
- builder: (context) {
- return CustomDialogBox(
- title: AdvertTextConstants.deleting,
- descriptions: AdvertTextConstants.deleteAdvert,
- onYes: () {
- advertListNotifier.deleteAdvert(advert);
- advertPostersNotifier.deleteE(advert.id, 0);
- },
- );
- },
- );
- },
- advert: advert,
+ icon: HeroIcon(
+ HeroIcons.plus,
+ color: ColorConstants.background,
),
+ name: "Post",
),
+ SizedBox(width: 10),
],
- );
- },
- ),
+ ],
+ ),
+ const SizedBox(height: 20),
+ Expanded(
+ child: AsyncChild(
+ value: advertList,
+ builder: (context, advertData) {
+ final userAssociationAdvert = advertData.where(
+ (advert) => !isAdmin
+ ? myAssociationList.any(
+ (element) => advert.associationId == element.id,
+ )
+ : true,
+ );
+ final sortedUserAssociationAdverts = userAssociationAdvert
+ .toList()
+ .sortedBy((element) => element.date)
+ .reversed;
+ final filteredSortedUserAssociationAdverts =
+ sortedUserAssociationAdverts
+ .where(
+ (advert) =>
+ selectedAssociations
+ .where((e) => advert.associationId == e.id)
+ .isNotEmpty ||
+ selectedAssociations.isEmpty,
+ )
+ .toList();
+ return Refresher(
+ controller: ScrollController(),
+ onRefresh: () async {
+ if (isAdmin) {
+ await ref
+ .watch(advertListProvider.notifier)
+ .loadAdverts();
+ }
+ await ref.watch(advertListProvider.notifier).loadAdverts();
+ await myAssociationListNotifier.loadAssociations();
+ advertPostersNotifier.resetTData();
+ },
+ child: Column(
+ children: [
+ ...filteredSortedUserAssociationAdverts.map(
+ (advert) => AdminAdvertCard(
+ onEdit: () {
+ QR.to(
+ AdvertRouter.root +
+ AdvertRouter.admin +
+ AdvertRouter.addEditAdvert,
+ );
+ advertNotifier.setAdvert(advert);
+ selectedAssociationsNotifier.clearAssociation();
+ selectedAssociationsNotifier.addAssociation(
+ myAssociationList.firstWhere(
+ (element) => element.id == advert.associationId,
+ ),
+ );
+ },
+ onDelete: () async {
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return CustomDialogBox(
+ title: AppLocalizations.of(
+ context,
+ )!.advertDeleting,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.advertDeleteAdvert,
+ onYes: () async {
+ if (isAdmin) {
+ await ref
+ .watch(advertListProvider.notifier)
+ .deleteAdvert(advert);
+ } else {
+ await ref
+ .watch(advertListProvider.notifier)
+ .deleteAdvert(advert);
+ }
+ advertPostersNotifier.deleteE(advert.id, 0);
+ },
+ );
+ },
+ );
+ },
+ advert: advert,
+ ),
+ ),
+ SizedBox(height: 80),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ ],
),
);
}
diff --git a/lib/advert/ui/pages/advert.dart b/lib/advert/ui/pages/advert.dart
index f737e5dc07..966d6777d0 100644
--- a/lib/advert/ui/pages/advert.dart
+++ b/lib/advert/ui/pages/advert.dart
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/advert/providers/announcer_provider.dart';
+import 'package:titan/advert/providers/selected_association_provider.dart';
+import 'package:titan/tools/constants.dart';
import 'package:titan/advert/router.dart';
-import 'package:titan/advert/tools/constants.dart';
import 'package:titan/tools/ui/widgets/top_bar.dart';
class AdvertTemplate extends HookConsumerWidget {
@@ -11,21 +11,21 @@ class AdvertTemplate extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
- final selectedAnnouncersNotifier = ref.read(announcerProvider.notifier);
+ final selectedAssociationsNotifier = ref.read(
+ selectedAssociationProvider.notifier,
+ );
return Scaffold(
body: Container(
- color: Colors.white,
+ color: ColorConstants.background,
child: SafeArea(
child: Column(
children: [
TopBar(
- title: AdvertTextConstants.advert,
root: AdvertRouter.root,
onBack: () {
- selectedAnnouncersNotifier.clearAnnouncer();
+ selectedAssociationsNotifier.clearAssociation();
},
),
- const SizedBox(height: 30),
Expanded(child: child),
],
),
diff --git a/lib/advert/ui/pages/detail_page/detail.dart b/lib/advert/ui/pages/detail_page/detail.dart
deleted file mode 100644
index 643d28fb42..0000000000
--- a/lib/advert/ui/pages/detail_page/detail.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-import 'package:auto_size_text/auto_size_text.dart';
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:intl/intl.dart';
-import 'package:titan/advert/providers/advert_poster_provider.dart';
-import 'package:titan/advert/providers/advert_posters_provider.dart';
-import 'package:titan/advert/providers/advert_provider.dart';
-import 'package:titan/advert/ui/components/tag_chip.dart';
-import 'package:titan/cinema/tools/functions.dart';
-import 'package:titan/tools/ui/builders/auto_loader_child.dart';
-import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
-import 'package:titan/tools/ui/widgets/text_with_hyper_link.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class AdvertDetailPage extends HookConsumerWidget {
- const AdvertDetailPage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final advert = ref.watch(advertProvider);
- final posters = ref.watch(
- advertPostersProvider.select((advertPosters) => advertPosters[advert.id]),
- );
- final advertPostersNotifier = ref.watch(advertPostersProvider.notifier);
- final logoNotifier = ref.watch(advertPosterProvider.notifier);
- final filteredTagList = advert.tags
- .where((element) => element != "")
- .toList();
- final inTagChipsList = [advert.announcer.name] + filteredTagList;
-
- return Stack(
- children: [
- Container(
- width: double.infinity,
- decoration: const BoxDecoration(
- boxShadow: [
- BoxShadow(
- color: Colors.black26,
- blurRadius: 10,
- spreadRadius: 7,
- offset: Offset(0, 5),
- ),
- ],
- ),
- child: AutoLoaderChild(
- group: posters,
- notifier: advertPostersNotifier,
- mapKey: advert,
- loader: (ref) => logoNotifier.getAdvertPoster(advert.id),
- dataBuilder: (context, value) =>
- Image(image: value.first.image, fit: BoxFit.fill),
- ),
- ),
- SingleChildScrollView(
- physics: const BouncingScrollPhysics(),
- child: Column(
- children: [
- const SizedBox(height: 220),
- Container(
- width: double.infinity,
- height: 50,
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- const Color.fromARGB(0, 255, 255, 255),
- Colors.grey.shade50.withValues(alpha: 0.85),
- Colors.grey.shade50,
- ],
- stops: const [0.0, 0.65, 1.0],
- ),
- ),
- ),
- Container(
- color: Colors.grey.shade50,
- child: Column(
- children: [
- const SizedBox(height: 15),
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- alignment: Alignment.center,
- child: AutoSizeText(
- advert.title,
- maxLines: 2,
- textAlign: TextAlign.center,
- style: const TextStyle(
- fontSize: 30,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- const SizedBox(height: 15),
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- alignment: Alignment.center,
- child: Text(
- formatDate(advert.date),
- style: const TextStyle(fontSize: 18),
- ),
- ),
- const SizedBox(height: 20),
- HorizontalListView.builder(
- height: 35,
- horizontalSpace: 30,
- items: inTagChipsList,
- itemBuilder:
- (BuildContext context, String item, int index) =>
- TagChip(tagName: item),
- ),
- const SizedBox(height: 15),
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: TextWithHyperLink(
- advert.content,
- textAlign: TextAlign.left,
- style: const TextStyle(fontSize: 15),
- ),
- ),
- const SizedBox(height: 140),
- ],
- ),
- ),
- ],
- ),
- ),
- Column(
- children: [
- const SizedBox(height: 45),
- Row(
- children: [
- const SizedBox(width: 20),
- GestureDetector(
- onTap: QR.back,
- child: Container(
- padding: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- color: Colors.white.withValues(alpha: 0.9),
- borderRadius: BorderRadius.circular(18),
- boxShadow: [
- BoxShadow(
- color: Colors.white.withValues(alpha: 0.3),
- blurRadius: 7,
- spreadRadius: 2,
- offset: const Offset(2, 3),
- ),
- ],
- ),
- child: const Icon(Icons.arrow_back, color: Colors.black),
- ),
- ),
- const Spacer(),
- Container(
- padding: const EdgeInsets.symmetric(
- vertical: 8,
- horizontal: 12,
- ),
- decoration: BoxDecoration(
- color: Colors.white.withValues(alpha: 0.9),
- borderRadius: BorderRadius.circular(18),
- boxShadow: [
- BoxShadow(
- color: Colors.white.withValues(alpha: 0.3),
- blurRadius: 7,
- spreadRadius: 2,
- offset: const Offset(2, 3),
- ),
- ],
- ),
- child: Row(
- children: [
- const HeroIcon(HeroIcons.calendar, size: 20),
- const SizedBox(width: 7),
- Text(
- DateFormat('dd/MM/yyyy - HH:mm').format(advert.date),
- style: const TextStyle(fontSize: 16),
- textAlign: TextAlign.center,
- ),
- ],
- ),
- ),
- const SizedBox(width: 20),
- ],
- ),
- ],
- ),
- ],
- );
- }
-}
diff --git a/lib/advert/ui/pages/form_page/add_edit_advert_page.dart b/lib/advert/ui/pages/form_page/add_edit_advert_page.dart
index 0cac9788ce..5a383a5601 100644
--- a/lib/advert/ui/pages/form_page/add_edit_advert_page.dart
+++ b/lib/advert/ui/pages/form_page/add_edit_advert_page.dart
@@ -6,23 +6,25 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:titan/admin/class/assocation.dart';
import 'package:titan/advert/class/advert.dart';
-import 'package:titan/advert/class/announcer.dart';
import 'package:titan/advert/providers/advert_list_provider.dart';
import 'package:titan/advert/providers/advert_poster_provider.dart';
import 'package:titan/advert/providers/advert_posters_provider.dart';
import 'package:titan/advert/providers/advert_provider.dart';
-import 'package:titan/advert/providers/announcer_provider.dart';
-import 'package:titan/advert/tools/constants.dart';
+import 'package:titan/advert/providers/selected_association_provider.dart';
import 'package:titan/advert/ui/pages/advert.dart';
-import 'package:titan/advert/ui/components/announcer_bar.dart';
+import 'package:titan/advert/ui/components/association_bar.dart';
+import 'package:titan/event/ui/pages/event_pages/checkbox_entry.dart';
+import 'package:titan/navigation/ui/scroll_to_hide_navbar.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/tools/ui/layouts/add_edit_button_layout.dart';
+import 'package:titan/tools/ui/styleguide/text_entry.dart';
import 'package:titan/tools/ui/widgets/image_picker_on_tap.dart';
-import 'package:titan/tools/ui/widgets/text_entry.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AdvertAddEditAdvertPage extends HookConsumerWidget {
const AdvertAddEditAdvertPage({super.key});
@@ -34,17 +36,15 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
final isEdit = advert.id != Advert.empty().id;
final title = useTextEditingController(text: advert.title);
final content = useTextEditingController(text: advert.content);
- final selectedAnnouncers = ref.watch(announcerProvider);
- final tags = advert.tags;
- var textTags = tags.join(', ');
- final textTagsController = useTextEditingController(text: textTags);
final advertPosters = ref.watch(advertPostersProvider);
final advertListNotifier = ref.watch(advertListProvider.notifier);
final posterNotifier = ref.watch(advertPosterProvider.notifier);
final poster = useState(null);
final posterFile = useState(null);
+ final selectedAssociation = ref.watch(selectedAssociationProvider);
+
if (advertPosters[advert.id] != null) {
advertPosters[advert.id]!.whenData((data) {
if (data.isNotEmpty) {
@@ -53,6 +53,9 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
});
}
+ final postToFeed = useState(isEdit ? advert.postToFeed : false);
+ final notification = useState(isEdit ? advert.notification : true);
+
final ImagePicker picker = ImagePicker();
void displayAdvertToastWithContext(TypeMsg type, String msg) {
@@ -60,26 +63,60 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
}
return AdvertTemplate(
- child: SingleChildScrollView(
- physics: const BouncingScrollPhysics(),
+ child: ScrollToHideNavbar(
+ controller: ScrollController(),
child: Form(
key: key,
child: Column(
children: [
+ FormField>(
+ validator: (e) {
+ if (selectedAssociation.isEmpty) {
+ return AppLocalizations.of(
+ context,
+ )!.advertChoosingAnnouncer;
+ }
+ return null;
+ },
+ builder: (formFieldState) => Container(
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: const BorderRadius.all(Radius.circular(20)),
+ boxShadow: formFieldState.hasError
+ ? [
+ const BoxShadow(
+ color: Colors.red,
+ spreadRadius: 3,
+ blurRadius: 3,
+ offset: Offset(2, 2),
+ ),
+ ]
+ : [],
+ ),
+ child: AssociationBar(
+ useUserAssociations: true,
+ multipleSelect: false,
+ isNotClickable: isEdit,
+ ),
+ ),
+ ),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
children: [
+ const SizedBox(height: 20),
TextEntry(
maxLines: 1,
- label: AdvertTextConstants.title,
+ label: AppLocalizations.of(context)!.advertTitle,
controller: title,
),
const SizedBox(height: 20),
FormField(
validator: (e) {
if (poster.value == null && !isEdit) {
- return AdvertTextConstants.choosingPoster;
+ return AppLocalizations.of(
+ context,
+ )!.advertChoosingPoster;
}
return null;
},
@@ -93,144 +130,161 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
imageNotifier: posterFile,
displayToastWithContext:
displayAdvertToastWithContext,
- child: Container(
- decoration: BoxDecoration(
- color: Colors.white,
- boxShadow: [
- BoxShadow(
- color: formFieldState.hasError
- ? Colors.red
- : Colors.black.withValues(alpha: 0.1),
- spreadRadius: 5,
- blurRadius: 10,
- offset: const Offset(2, 3),
+ child: AspectRatio(
+ aspectRatio: 851 / 315,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(5),
),
- ],
- ),
- child: posterFile.value != null
- ? Stack(
- children: [
- Container(
- width: 285,
- height: 160,
- decoration: BoxDecoration(
- borderRadius:
- const BorderRadius.all(
- Radius.circular(5),
- ),
- image: DecorationImage(
- image: poster.value != null
- ? Image.memory(
- poster.value!,
- fit: BoxFit.cover,
- ).image
- : posterFile.value!.image,
- fit: BoxFit.cover,
+ color: Colors.white,
+ boxShadow: [
+ BoxShadow(
+ color: formFieldState.hasError
+ ? Colors.red
+ : Colors.black.withValues(
+ alpha: 0.1,
),
- ),
- child: Center(
- child: Container(
- width: 50,
- height: 50,
- decoration: BoxDecoration(
- borderRadius:
- const BorderRadius.all(
- Radius.circular(5),
- ),
- color: Colors.white
- .withValues(alpha: 0.4),
+ spreadRadius: 5,
+ blurRadius: 10,
+ offset: const Offset(2, 3),
+ ),
+ ],
+ ),
+ child: posterFile.value != null
+ ? Stack(
+ children: [
+ Container(
+ decoration: BoxDecoration(
+ borderRadius:
+ const BorderRadius.all(
+ Radius.circular(5),
+ ),
+ image: DecorationImage(
+ image: poster.value != null
+ ? Image.memory(
+ poster.value!,
+ fit: BoxFit.cover,
+ ).image
+ : posterFile.value!.image,
+ fit: BoxFit.cover,
),
- child: HeroIcon(
- HeroIcons.photo,
- size: 40,
- color: Colors.black
- .withValues(alpha: 0.5),
+ ),
+ child: Center(
+ child: Container(
+ width: 50,
+ height: 50,
+ decoration: BoxDecoration(
+ borderRadius:
+ const BorderRadius.all(
+ Radius.circular(5),
+ ),
+ color: Colors.white
+ .withValues(alpha: 0.4),
+ ),
+ child: HeroIcon(
+ HeroIcons.photo,
+ size: 40,
+ color: Colors.black
+ .withValues(alpha: 0.5),
+ ),
),
),
),
+ ],
+ )
+ : Container(
+ decoration: BoxDecoration(
+ borderRadius:
+ const BorderRadius.all(
+ Radius.circular(5),
+ ),
),
- ],
- )
- : const HeroIcon(
- HeroIcons.photo,
- size: 160,
- color: Colors.grey,
- ),
+ child: Column(
+ mainAxisAlignment:
+ MainAxisAlignment.start,
+ children: [
+ const HeroIcon(
+ HeroIcons.photo,
+ size: 100,
+ color: Colors.grey,
+ ),
+ Text("(851/315)"),
+ ],
+ ),
+ ),
+ ),
),
),
],
),
),
),
+ const SizedBox(height: 20),
TextEntry(
minLines: 5,
- maxLines: 50,
keyboardType: TextInputType.multiline,
- label: AdvertTextConstants.content,
+ label: AppLocalizations.of(context)!.advertContent,
controller: content,
),
],
),
),
- const SizedBox(height: 50),
- FormField>(
- validator: (e) {
- if (selectedAnnouncers.isEmpty) {
- return AdvertTextConstants.choosingAnnouncer;
- }
- return null;
- },
- builder: (formFieldState) => Container(
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: const BorderRadius.all(Radius.circular(20)),
- boxShadow: formFieldState.hasError
- ? [
- const BoxShadow(
- color: Colors.red,
- spreadRadius: 3,
- blurRadius: 3,
- offset: Offset(2, 2),
- ),
- ]
- : [],
- ),
- child: AnnouncerBar(
- useUserAnnouncers: true,
- multipleSelect: false,
- isNotClickable: isEdit,
- ),
+ const SizedBox(height: 20),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30.0),
+ child: CheckBoxEntry(
+ title: AppLocalizations.of(context)!.advertPublishToFeed,
+ valueNotifier: postToFeed,
+ onChanged: () {
+ postToFeed.value = !postToFeed.value;
+ },
+ ),
+ ),
+ const SizedBox(height: 10),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30.0),
+ child: CheckBoxEntry(
+ title: AppLocalizations.of(context)!.advertNotification,
+ valueNotifier: notification,
+ onChanged: () {
+ notification.value = !notification.value;
+ },
),
),
+ const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
children: [
- TextEntry(
- maxLines: 1,
- label: AdvertTextConstants.tags,
- canBeEmpty: true,
- controller: textTagsController,
- ),
- const SizedBox(height: 50),
WaitingButton(
onTap: () async {
if (key.currentState == null) {
return;
}
if (key.currentState!.validate() &&
- selectedAnnouncers.isNotEmpty &&
+ selectedAssociation.isNotEmpty &&
(poster.value != null || isEdit)) {
await tokenExpireWrapper(ref, () async {
final advertList = ref.watch(advertListProvider);
Advert newAdvert = Advert(
id: isEdit ? advert.id : '',
- announcer: selectedAnnouncers[0],
+ associationId: selectedAssociation[0].id,
content: content.text,
date: isEdit ? advert.date : DateTime.now(),
- tags: textTagsController.text.split(', '),
title: title.text,
+ postToFeed: postToFeed.value,
+ notification: notification.value,
);
+ final editedAdvertMsg = AppLocalizations.of(
+ context,
+ )!.advertEditedAdvert;
+ final addedAdvertMsg = AppLocalizations.of(
+ context,
+ )!.advertAddedAdvert;
+ final editingErrorMsg = AppLocalizations.of(
+ context,
+ )!.advertEditingError;
final value = isEdit
? await advertListNotifier.updateAdvert(
newAdvert,
@@ -241,7 +295,7 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
if (isEdit) {
displayAdvertToastWithContext(
TypeMsg.msg,
- AdvertTextConstants.editedAdvert,
+ editedAdvertMsg,
);
advertList.maybeWhen(
data: (list) {
@@ -257,7 +311,7 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
} else {
displayAdvertToastWithContext(
TypeMsg.msg,
- AdvertTextConstants.addedAdvert,
+ addedAdvertMsg,
);
advertList.maybeWhen(
data: (list) {
@@ -273,7 +327,7 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
} else {
displayAdvertToastWithContext(
TypeMsg.error,
- AdvertTextConstants.editingError,
+ editingErrorMsg,
);
}
});
@@ -281,14 +335,16 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- AdvertTextConstants.incorrectOrMissingFields,
+ AppLocalizations.of(
+ context,
+ )!.advertIncorrectOrMissingFields,
);
}
},
child: Text(
isEdit
- ? AdvertTextConstants.edit
- : AdvertTextConstants.add,
+ ? AppLocalizations.of(context)!.advertEdit
+ : AppLocalizations.of(context)!.advertAdd,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
@@ -300,7 +356,7 @@ class AdvertAddEditAdvertPage extends HookConsumerWidget {
],
),
),
- const SizedBox(height: 20),
+ const SizedBox(height: 100),
],
),
),
diff --git a/lib/advert/ui/pages/form_page/add_rem_announcer_page.dart b/lib/advert/ui/pages/form_page/add_rem_announcer_page.dart
deleted file mode 100644
index f3843d7288..0000000000
--- a/lib/advert/ui/pages/form_page/add_rem_announcer_page.dart
+++ /dev/null
@@ -1,177 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/group_list_provider.dart';
-import 'package:titan/advert/class/announcer.dart';
-import 'package:titan/advert/providers/all_announcer_list_provider.dart';
-import 'package:titan/advert/providers/announcer_list_provider.dart';
-import 'package:titan/advert/tools/constants.dart';
-import 'package:titan/advert/ui/pages/advert.dart';
-import 'package:titan/advert/ui/pages/form_page/announcer_card.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/token_expire_wrapper.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
-
-class AddRemAnnouncerPage extends HookConsumerWidget {
- const AddRemAnnouncerPage({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final announcerListNotifier = ref.watch(announcerListProvider.notifier);
- final announcers = ref.watch(allAnnouncerList);
- final groups = ref.watch(allGroupListProvider);
- final announcerIds = announcers.map((x) => x.groupManagerId).toList();
-
- void displayToastWithContext(TypeMsg type, String msg) {
- displayToast(context, type, msg);
- }
-
- return AdvertTemplate(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30.0),
- child: SingleChildScrollView(
- physics: const AlwaysScrollableScrollPhysics(
- parent: BouncingScrollPhysics(),
- ),
- child: Column(
- children: [
- SizedBox(
- child: Column(
- children: [
- const Align(
- alignment: Alignment.centerLeft,
- child: Text(
- AdvertTextConstants.modifyAnnouncingGroup,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.w700,
- color: ColorConstants.gradient1,
- ),
- ),
- ),
- const SizedBox(height: 30),
- AsyncChild(
- value: groups,
- builder: (context, groupList) {
- final canAdd = groupList
- .where((x) => !announcerIds.contains(x.id))
- .toList();
- final canRemove = groupList
- .where((x) => announcerIds.contains(x.id))
- .toList();
- return (canAdd + canRemove).isNotEmpty
- ? Column(
- children:
- canAdd
- .map(
- (e) => GestureDetector(
- onTap: () {
- Announcer newAnnouncer =
- Announcer(
- groupManagerId: e.id,
- id: '',
- name: e.name,
- );
- tokenExpireWrapper(ref, () async {
- final value =
- await announcerListNotifier
- .addAnnouncer(
- newAnnouncer,
- );
- if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdvertTextConstants
- .addedAnnouncer,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdvertTextConstants
- .addingError,
- );
- }
- announcerListNotifier
- .loadAllAnnouncerList();
- });
- },
- child: AnnouncerCard(
- e: e,
- icon: HeroIcons.plus,
- ),
- ),
- )
- .toList() +
- canRemove
- .map(
- (e) => GestureDetector(
- onTap: () async {
- await showDialog(
- context: context,
- builder: (context) {
- return CustomDialogBox(
- title: AdvertTextConstants
- .deleting,
- descriptions:
- AdvertTextConstants
- .deleteAnnouncer,
- onYes: () {
- tokenExpireWrapper(ref, () async {
- final value = await announcerListNotifier
- .deleteAnnouncer(
- announcers
- .where(
- (element) =>
- e.id ==
- e.id,
- )
- .toList()[0],
- );
- if (value) {
- displayToastWithContext(
- TypeMsg.msg,
- AdvertTextConstants
- .removedAnnouncer,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AdvertTextConstants
- .removingError,
- );
- }
- announcerListNotifier
- .loadAllAnnouncerList();
- });
- },
- );
- },
- );
- },
- child: AnnouncerCard(
- e: e,
- icon: HeroIcons.minus,
- ),
- ),
- )
- .toList(),
- )
- : const Center(
- child: Text(
- AdvertTextConstants.noMoreAnnouncer,
- ),
- );
- },
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/advert/ui/pages/main_page/advert_card.dart b/lib/advert/ui/pages/main_page/advert_card.dart
new file mode 100644
index 0000000000..6f96b5e91e
--- /dev/null
+++ b/lib/advert/ui/pages/main_page/advert_card.dart
@@ -0,0 +1,258 @@
+import 'package:collection/collection.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:heroicons/heroicons.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/assocation_list_provider.dart';
+import 'package:titan/admin/providers/association_logo_provider.dart';
+import 'package:titan/admin/providers/associations_logo_map_provider.dart';
+import 'package:titan/advert/class/advert.dart';
+import 'package:titan/advert/providers/advert_poster_provider.dart';
+import 'package:titan/advert/providers/advert_posters_provider.dart';
+import 'package:titan/tools/constants.dart';
+import 'package:titan/tools/ui/builders/auto_loader_child.dart';
+import 'package:timeago/timeago.dart' as timeago;
+import 'package:url_launcher/url_launcher.dart';
+
+class AdvertCard extends HookConsumerWidget {
+ final Advert advert;
+
+ const AdvertCard({super.key, required this.advert});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final isExpanded = useState(false);
+ final posters = ref.watch(
+ advertPostersProvider.select((advertPosters) => advertPosters[advert.id]),
+ );
+ final advertPostersNotifier = ref.watch(advertPostersProvider.notifier);
+ final posterNotifier = ref.watch(advertPosterProvider.notifier);
+ final asyncAssociationList = ref.watch(associationListProvider);
+ final associationList = asyncAssociationList.when(
+ data: (data) => data,
+ loading: () => [],
+ error: (_, _) => [],
+ );
+ final associationName =
+ associationList
+ .firstWhereOrNull((e) => e.id == advert.associationId)
+ ?.name ??
+ '';
+ final associationLogo = ref.watch(
+ associationLogoMapProvider.select((value) => value[advert.associationId]),
+ );
+ final associationLogoMapNotifier = ref.watch(
+ associationLogoMapProvider.notifier,
+ );
+ final associationLogoNotifier = ref.watch(associationLogoProvider.notifier);
+ return Container(
+ margin: const EdgeInsets.all(10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
+ child: Row(
+ children: [
+ Center(
+ child: AutoLoaderChild(
+ group: associationLogo,
+ notifier: associationLogoMapNotifier,
+ mapKey: advert.associationId,
+ loader: (associationId) => associationLogoNotifier
+ .getAssociationLogo(associationId),
+ dataBuilder: (context, data) {
+ return CircleAvatar(
+ radius: 20,
+ backgroundColor: Colors.white,
+ backgroundImage: Image(image: data.first.image).image,
+ );
+ },
+ orElseBuilder: (context, stack) => Container(
+ width: 40,
+ height: 40,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle,
+ color: Colors.black,
+ ),
+ child: Text(
+ associationName
+ .split(' ')
+ .take(2)
+ .map((s) => s[0].toUpperCase())
+ .join(),
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 12),
+
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ associationName,
+ style: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ color: Colors.black,
+ ),
+ ),
+ Text(
+ _capitalizeFirst(
+ timeago.format(advert.date, locale: 'fr_short'),
+ ),
+ style: const TextStyle(
+ fontSize: 12,
+ color: Colors.grey,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 15),
+
+ AspectRatio(
+ aspectRatio: 851 / 315,
+ child: AutoLoaderChild(
+ group: posters,
+ notifier: advertPostersNotifier,
+ mapKey: advert.id,
+ loader: (advertId) => posterNotifier.getAdvertPoster(advertId),
+ loadingBuilder: (context) => Container(
+ decoration: BoxDecoration(
+ color: ColorConstants.onBackground,
+ borderRadius: BorderRadius.circular(20),
+ ),
+ child: const Center(child: HeroIcon(HeroIcons.photo, size: 50)),
+ ),
+ dataBuilder: (context, value) => Container(
+ decoration: BoxDecoration(
+ color: ColorConstants.onBackground,
+ borderRadius: BorderRadius.circular(20),
+ image: DecorationImage(
+ image: value.first.image,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ ),
+
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [_buildExpandableText(isExpanded)],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ String _capitalizeFirst(String text) {
+ if (text.isEmpty) return text;
+ return text[0].toUpperCase() + text.substring(1);
+ }
+
+ Widget _buildExpandableText(ValueNotifier isExpanded) {
+ final title = advert.title.trim();
+ final content = advert.content.trim();
+
+ final fullText = title.isNotEmpty ? '$title $content' : content;
+
+ const maxLength = 100;
+
+ final isLong = fullText.length > maxLength;
+
+ String displayContent;
+ if (isLong && !isExpanded.value) {
+ displayContent = '${content.substring(0, maxLength)}...';
+ } else {
+ displayContent = content;
+ }
+
+ final words = displayContent.split(' ');
+ final List spans = [];
+
+ if (title.isNotEmpty) {
+ spans.add(
+ TextSpan(
+ text: title,
+ style: const TextStyle(fontWeight: FontWeight.bold),
+ ),
+ );
+ spans.add(const TextSpan(text: ' '));
+ }
+
+ for (int i = 0; i < words.length; i++) {
+ final word = words[i];
+ final isLink = word.startsWith('https://') || word.startsWith('http://');
+
+ if (isLink) {
+ spans.add(
+ TextSpan(
+ text: word,
+ style: const TextStyle(
+ color: ColorConstants.main,
+ decoration: TextDecoration.underline,
+ ),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () async {
+ final uri = Uri.parse(word);
+ if (await canLaunchUrl(uri)) {
+ await launchUrl(uri, mode: LaunchMode.externalApplication);
+ }
+ },
+ ),
+ );
+ } else {
+ spans.add(TextSpan(text: word));
+ }
+
+ if (i < words.length - 1) {
+ spans.add(const TextSpan(text: ' '));
+ }
+ }
+
+ if (isLong) {
+ spans.add(const TextSpan(text: ' '));
+ spans.add(
+ WidgetSpan(
+ alignment: PlaceholderAlignment.middle,
+ child: GestureDetector(
+ onTap: () {
+ isExpanded.value = !isExpanded.value;
+ },
+ child: Text(
+ isExpanded.value ? 'voir moins' : 'voir plus',
+ style: const TextStyle(
+ color: Colors.grey,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ return RichText(
+ text: TextSpan(
+ style: const TextStyle(color: Colors.black, fontSize: 14, height: 1.4),
+ children: spans,
+ ),
+ );
+ }
+}
diff --git a/lib/advert/ui/pages/main_page/main_page.dart b/lib/advert/ui/pages/main_page/main_page.dart
index 0479c2bdc1..6901e97154 100644
--- a/lib/advert/ui/pages/main_page/main_page.dart
+++ b/lib/advert/ui/pages/main_page/main_page.dart
@@ -1,105 +1,104 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
+import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/admin/providers/is_admin_provider.dart';
import 'package:titan/advert/providers/advert_list_provider.dart';
import 'package:titan/advert/providers/advert_posters_provider.dart';
-import 'package:titan/advert/providers/advert_provider.dart';
-import 'package:titan/advert/providers/announcer_provider.dart';
-import 'package:titan/advert/providers/is_advert_admin_provider.dart';
+import 'package:titan/advert/providers/selected_association_provider.dart';
+import 'package:titan/advert/ui/components/special_action_button.dart';
import 'package:titan/advert/ui/pages/advert.dart';
import 'package:titan/advert/router.dart';
-import 'package:titan/advert/ui/components/announcer_bar.dart';
-import 'package:titan/advert/ui/components/advert_card.dart';
+import 'package:titan/advert/ui/components/association_bar.dart';
+import 'package:titan/advert/ui/pages/main_page/advert_card.dart';
+import 'package:titan/feed/providers/is_user_a_member_of_an_association.dart';
+import 'package:titan/tools/constants.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/tools/ui/layouts/column_refresher.dart';
-import 'package:titan/tools/ui/widgets/admin_button.dart';
+import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:qlevar_router/qlevar_router.dart';
-import 'package:titan/advert/tools/constants.dart';
class AdvertMainPage extends HookConsumerWidget {
const AdvertMainPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
- final advertNotifier = ref.watch(advertProvider.notifier);
final advertList = ref.watch(advertListProvider);
final advertListNotifier = ref.watch(advertListProvider.notifier);
final advertPostersNotifier = ref.watch(advertPostersProvider.notifier);
- final selected = ref.watch(announcerProvider);
- final selectedNotifier = ref.watch(announcerProvider.notifier);
+ final selected = ref.watch(selectedAssociationProvider);
+ final selectedNotifier = ref.watch(selectedAssociationProvider.notifier);
+ final isAdvertAdmin = ref.watch(isUserAMemberOfAnAssociationProvider);
final isAdmin = ref.watch(isAdminProvider);
- final isAdvertAdmin = ref.watch(isAdvertAdminProvider);
return AdvertTemplate(
- child: Stack(
+ child: Column(
children: [
- AsyncChild(
- value: advertList,
- builder: (context, advertData) {
- final sortedAdvertData = advertData
- .sortedBy((element) => element.date)
- .reversed;
- final filteredSortedAdvertData = sortedAdvertData.where(
- (advert) =>
- selected
- .where((e) => advert.announcer.name == e.name)
- .isNotEmpty ||
- selected.isEmpty,
- );
- return ColumnRefresher(
- onRefresh: () async {
- await advertListNotifier.loadAdverts();
- advertPostersNotifier.resetTData();
- },
- children: [
- SizedBox(
- width: 300,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- if (isAdvertAdmin)
- AdminButton(
- onTap: () {
- selectedNotifier.clearAnnouncer();
- QR.to(AdvertRouter.root + AdvertRouter.admin);
- },
- ),
- if (isAdmin)
- AdminButton(
- onTap: () {
- QR.to(
- AdvertRouter.root +
- AdvertRouter.addRemAnnouncer,
- );
- },
- text: AdvertTextConstants.management,
- ),
- ],
- ),
- ),
- const SizedBox(height: 20),
- const AnnouncerBar(
- useUserAnnouncers: false,
- multipleSelect: true,
+ Row(
+ children: [
+ Expanded(
+ child: const AssociationBar(
+ useUserAssociations: false,
+ multipleSelect: true,
+ ),
+ ),
+
+ if (isAdmin || isAdvertAdmin) ...[
+ SizedBox(width: 5),
+ Container(
+ width: 2,
+ height: 60,
+ color: ColorConstants.secondary,
+ ),
+ SizedBox(width: 5),
+ SpecialActionButton(
+ onTap: () {
+ selectedNotifier.clearAssociation();
+ QR.to(AdvertRouter.root + AdvertRouter.admin);
+ },
+ icon: HeroIcon(
+ HeroIcons.userGroup,
+ color: ColorConstants.background,
),
- const SizedBox(height: 20),
- ...filteredSortedAdvertData.map(
- (advert) => Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30),
- child: AdvertCard(
- onTap: () {
- advertNotifier.setAdvert(advert);
- QR.to(AdvertRouter.root + AdvertRouter.detail);
- },
- advert: advert,
+ name: "Admin",
+ ),
+ SizedBox(width: 10),
+ ],
+ ],
+ ),
+
+ const SizedBox(height: 20),
+
+ Expanded(
+ child: AsyncChild(
+ value: advertList,
+ builder: (context, advertData) {
+ final sortedAdvertData = advertData
+ .sortedBy((element) => element.date)
+ .reversed;
+ final filteredSortedAdvertData = sortedAdvertData.where(
+ (advert) =>
+ selected
+ .where((e) => advert.associationId == e.id)
+ .isNotEmpty ||
+ selected.isEmpty,
+ );
+ return Refresher(
+ controller: ScrollController(),
+ onRefresh: () async {
+ await advertListNotifier.loadAdverts();
+ advertPostersNotifier.resetTData();
+ },
+ child: Column(
+ children: [
+ ...filteredSortedAdvertData.map(
+ (advert) => AdvertCard(advert: advert),
),
- ),
+ SizedBox(height: 80),
+ ],
),
- ],
- );
- },
+ );
+ },
+ ),
),
- const SizedBox(height: 20),
],
),
);
diff --git a/lib/amap/providers/is_amap_admin_provider.dart b/lib/amap/providers/is_amap_admin_provider.dart
index ecc2ab30e8..dbfaab9153 100644
--- a/lib/amap/providers/is_amap_admin_provider.dart
+++ b/lib/amap/providers/is_amap_admin_provider.dart
@@ -5,5 +5,5 @@ final isAmapAdminProvider = StateProvider((ref) {
final me = ref.watch(userProvider);
return me.groups
.map((e) => e.id)
- .contains("70db65ee-d533-4f6b-9ffa-a4d70a17b7ef");
+ .contains("70db65ee-d533-4f6b-9ffa-a4d70a17b7ef"); // admin_amap
});
diff --git a/lib/amap/router.dart b/lib/amap/router.dart
index 47cfe0129f..80c43189ea 100644
--- a/lib/amap/router.dart
+++ b/lib/amap/router.dart
@@ -1,6 +1,5 @@
-import 'package:either_dart/either.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
import 'package:titan/amap/providers/is_amap_admin_provider.dart';
import 'package:titan/amap/ui/pages/admin_page/admin_page.dart'
deferred as admin_page;
@@ -18,7 +17,8 @@ import 'package:titan/amap/ui/pages/presentation_page/text.dart'
deferred as presentation_page;
import 'package:titan/amap/ui/pages/product_pages/add_edit_product.dart'
deferred as add_edit_product;
-import 'package:titan/drawer/class/module.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
import 'package:titan/tools/middlewares/admin_middleware.dart';
import 'package:titan/tools/middlewares/authenticated_middleware.dart';
import 'package:titan/tools/middlewares/deferred_middleware.dart';
@@ -35,10 +35,10 @@ class AmapRouter {
static const String presentation = '/presentation';
static const String addEditProduct = '/add_edit_product';
static final Module module = Module(
- name: "Amap",
- icon: const Left(HeroIcons.shoppingCart),
+ getName: (context) => AppLocalizations.of(context)!.moduleAmap,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleAmapDescription,
root: AmapRouter.root,
- selected: false,
);
AmapRouter(this.ref);
@@ -50,6 +50,10 @@ class AmapRouter {
AuthenticatedMiddleware(ref),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
children: [
QRoute(
path: admin,
diff --git a/lib/amap/tools/constants.dart b/lib/amap/tools/constants.dart
index db49483f8c..65fc60d714 100644
--- a/lib/amap/tools/constants.dart
+++ b/lib/amap/tools/constants.dart
@@ -20,133 +20,3 @@ class AMAPColorConstants extends ColorConstants {
static const Color redGradient1 = Color(0xFF9E131F);
static const Color redGradient2 = Color(0xFF590512);
}
-
-class AMAPTextConstants {
- static const String accounts = "Comptes";
- static const String add = "Ajouter";
- static const String addDelivery = "Ajouter une livraison";
- static const String addedCommand = "Commande ajoutée";
- static const String addedOrder = "Commande ajoutée";
- static const String addedProduct = "Produit ajouté";
- static const String addedUser = "Utilisateur ajouté";
- static const String addProduct = "Ajouter un produit";
- static const String addUser = "Ajouter un utilisateur";
- static const String addingACommand = "Ajouter une commande";
- static const String addingCommand = "Ajouter la commande";
- static const String addingError = "Erreur lors de l'ajout";
- static const String addingProduct = "Ajouter un produit";
- static const String addOrder = "Ajouter une commande";
- static const String admin = "Admin";
- static const String alreadyExistCommand =
- "Il existe déjà une commande à cette date";
- static const String amap = "Amap";
- static const String amount = "Solde";
- static const String archive = "Archiver";
- static const String archiveDelivery = "Archiver";
- static const String archivingDelivery = "Archivage de la livraison";
- static const String category = "Catégorie";
- static const String closeDelivery = "Verrouiller";
- static const String commandDate = "Date de la commande";
- static const String commandProducts = "Produits de la commande";
- static const String confirm = "Confirmer";
- static const String contact = "Contacts associatifs ";
- static const String createCategory = "Créer une catégorie";
- static const String delete = "Supprimer";
- static const String deleteDelivery = "Supprimer la livraison ?";
- static const String deleteDeliveryDescription =
- "Voulez-vous vraiment supprimer cette livraison ?";
- static const String deletedDelivery = "Livraison supprimée";
- static const String deletedOrder = "Commande supprimée";
- static const String deletedProduct = "Produit supprimé";
- static const String deleteProduct = "Supprimer le produit ?";
- static const String deleteProductDescription =
- "Voulez-vous vraiment supprimer ce produit ?";
- static const String deleting = "Suppression";
- static const String deletingDelivery = "Supprimer la livraison ?";
- static const String deletingError = "Erreur lors de la suppression";
- static const String deletingOrder = "Supprimer la commande ?";
- static const String deletingProduct = "Supprimer le produit ?";
- static const String deliver = "Livraison teminée ?";
- static const String deliveries = "Livraisons";
- static const String deliveringDelivery =
- "Toutes les commandes sont livrées ?";
- static const String delivery = "Livraison";
- static const String deliveryArchived = "Livraison archivée";
- static const String deliveryDate = "Date de livraison";
- static const String deliveryDelivered = "Livraison effectuée";
- static const String deliveryHistory = "Historique des livraisons";
- static const String deliveryList = "Liste des livraisons";
- static const String deliveryLocked = "Livraison verrouillée";
- static const String deliveryOn = "Livraison le";
- static const String deliveryOpened = "Livraison ouverte";
- static const String deliveryNotArchived = "Livraison non archivée";
- static const String deliveryNotLocked = "Livraison non verrouillée";
- static const String deliveryNotDelivered = "Livraison non effectuée";
- static const String deliveryNotOpened = "Livraison non ouverte";
- static const String editDelivery = "Modifier la livraison";
- static const String editedCommand = "Commande modifiée";
- static const String editingError = "Erreur lors de la modification";
- static const String editProduct = "Modifier le produit";
- static const String endingDelivery = "Fin de la livraison";
- static const String error = "Erreur";
- static const String errorLink = "Erreur lors de l'ouverture du lien";
- static const String errorLoadingUser =
- "Erreur lors du chargement des utilisateurs";
- static const String evening = "Soir";
- static const String expectingNumber = "Veuillez entrer un nombre";
- static const String fillField = "Veuillez remplir ce champ";
- static const String handlingAccount = "Gérer les comptes";
- static const String loading = "Chargement...";
- static const String loadingError = "Erreur lors du chargement";
- static const String lock = "Verrouiller";
- static const String locked = "Verrouillée";
- static const String lockedDelivery = "Livraison verrouillée";
- static const String lockedOrder = "Commande verrouillée";
- static const String looking = "Rechercher";
- static const String lockingDelivery = "Verrouiller la livraison ?";
- static const String midDay = "Midi";
- static const String myOrders = "Mes commandes";
- static const String name = "Nom";
- static const String nextStep = "Étape suivante";
- static const String noProduct = "Pas de produit";
- static const String noCurrentOrder = "Pas de commande en cours";
- static const String noMoney = "Pas assez d'argent";
- static const String noOpennedDelivery = "Pas de livraison ouverte";
- static const String noOrder = "Pas de commande";
- static const String noSelectedDelivery = "Pas de livraison sélectionnée";
- static const String notEnoughMoney = "Pas assez d'argent";
- static const String notPlannedDelivery = "Pas de livraison planifiée";
- static const String oneOrder = "commande";
- static const String openDelivery = "Ouvrir";
- static const String opened = "Ouverte";
- static const String openningDelivery = "Ouvrir la livraison ?";
- static const String order = "Commander";
- static const String orders = "Commandes";
- static const String pickChooseCategory =
- "Veuillez entrer une valeur ou choisir une catégorie existante";
- static const String pickDeliveryMoment = "Choisissez un moment de livraison";
- static const String presentation = "Présentation";
- static const String presentation1 =
- "L'AMAP (association pour le maintien d'une agriculture paysanne) est un service proposé par l'association Planet&Co de l'ECL. Vous pouvez ainsi recevoir des produits (paniers de fruits et légumes, jus, confitures...) directement sur le campus !\n\nLes commandes doivent être passées avant le vendredi 21h et sont livrées sur le campus le mardi de 13h à 13h45 (ou de 18h15 à 18h30 si vous ne pouvez pas passer le midi) dans le hall du M16.\n\nVous ne pouvez commander que si votre solde le permet. Vous pouvez recharger votre solde via la collecte Lydia ou bien avec un chèque que vous pouvez nous transmettre lors des permanences.\n\nLien vers la collecte Lydia pour le rechargement : ";
- static const String presentation2 =
- "\n\nN'hésitez pas à nous contacter en cas de problème !";
- static const String price = "Prix";
- static const String product = "produit";
- static const String products = "Produits";
- static const String productInDelivery =
- "Produit dans une livraison non terminée";
- static const String quantity = "Quantité";
- static const String requiredDate = "La date est requise";
- static const String seeMore = "Voir plus";
- static const String the = "Le";
- static const String unlock = "Dévérouiller";
- static const String unlockedDelivery = "Livraison dévérouillée";
- static const String unlockingDelivery = "Dévérouiller la livraison ?";
- static const String update = "Modifier";
- static const String updatedAmount = "Solde modifié";
- static const String updatedOrder = "Commande modifiée";
- static const String updatedProduct = "Produit modifié";
- static const String updatingError = "Echec de la modification";
- static const String usersNotFound = "Aucun utilisateur trouvé";
- static const String waiting = "En attente";
-}
diff --git a/lib/amap/tools/functions.dart b/lib/amap/tools/functions.dart
index a3b5eba7dc..11088e67d8 100644
--- a/lib/amap/tools/functions.dart
+++ b/lib/amap/tools/functions.dart
@@ -1,14 +1,15 @@
+import 'package:flutter/widgets.dart';
import 'package:titan/amap/class/delivery.dart';
import 'package:titan/amap/class/order.dart';
-import 'package:titan/amap/tools/constants.dart';
+import 'package:titan/l10n/app_localizations.dart';
// Slots in Titan UI must changed based on language
-String uiCollectionSlotToString(CollectionSlot slot) {
+String uiCollectionSlotToString(CollectionSlot slot, BuildContext context) {
switch (slot) {
case CollectionSlot.midDay:
- return AMAPTextConstants.midDay;
+ return AppLocalizations.of(context)!.amapMidDay;
case CollectionSlot.evening:
- return AMAPTextConstants.evening;
+ return AppLocalizations.of(context)!.amapEvening;
}
}
diff --git a/lib/amap/ui/amap.dart b/lib/amap/ui/amap.dart
index d2e8c6b9b9..dc52d09b93 100644
--- a/lib/amap/ui/amap.dart
+++ b/lib/amap/ui/amap.dart
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
+import 'package:qlevar_router/qlevar_router.dart';
import 'package:titan/amap/router.dart';
-import 'package:titan/amap/tools/constants.dart';
+import 'package:titan/tools/constants.dart';
import 'package:titan/tools/ui/widgets/top_bar.dart';
-import 'package:qlevar_router/qlevar_router.dart';
class AmapTemplate extends StatelessWidget {
final Widget child;
@@ -11,27 +11,29 @@ class AmapTemplate extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return SafeArea(
- child: Column(
- children: [
- TopBar(
- title: AMAPTextConstants.amap,
- root: AmapRouter.root,
- rightIcon: QR.currentPath == AmapRouter.root
- ? IconButton(
- onPressed: () {
- QR.to(AmapRouter.root + AmapRouter.presentation);
- },
- icon: const HeroIcon(
- HeroIcons.informationCircle,
- color: Colors.black,
- size: 40,
- ),
- )
- : null,
- ),
- Expanded(child: child),
- ],
+ return Container(
+ color: ColorConstants.background,
+ child: SafeArea(
+ child: Column(
+ children: [
+ TopBar(
+ root: AmapRouter.root,
+ rightIcon: QR.currentPath == AmapRouter.root
+ ? IconButton(
+ onPressed: () {
+ QR.to(AmapRouter.root + AmapRouter.presentation);
+ },
+ icon: const HeroIcon(
+ HeroIcons.informationCircle,
+ color: Colors.black,
+ size: 40,
+ ),
+ )
+ : null,
+ ),
+ Expanded(child: child),
+ ],
+ ),
),
);
}
diff --git a/lib/amap/ui/components/order_ui.dart b/lib/amap/ui/components/order_ui.dart
index 6c92e6bbd3..ca26eac62d 100644
--- a/lib/amap/ui/components/order_ui.dart
+++ b/lib/amap/ui/components/order_ui.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/amap/class/order.dart';
import 'package:titan/amap/providers/user_amount_provider.dart';
import 'package:titan/amap/providers/user_order_list_provider.dart';
@@ -13,6 +14,7 @@ import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/l10n/app_localizations.dart';
class OrderUI extends HookConsumerWidget {
final Order order;
@@ -29,6 +31,7 @@ class OrderUI extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final orderListNotifier = ref.watch(userOrderListProvider.notifier);
final orderNotifier = ref.watch(orderProvider.notifier);
final balanceNotifier = ref.watch(userAmountProvider.notifier);
@@ -53,7 +56,7 @@ class OrderUI extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- '${AMAPTextConstants.the} ${processDate(order.deliveryDate)}',
+ '${AppLocalizations.of(context)!.amapThe} ${DateFormat.yMd(locale).format(order.deliveryDate)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -78,7 +81,7 @@ class OrderUI extends HookConsumerWidget {
Row(
children: [
Text(
- "${order.products.length} ${AMAPTextConstants.product}${order.products.length != 1 ? "s" : ""}",
+ "${order.products.length} ${AppLocalizations.of(context)!.amapProduct}${order.products.length != 1 ? "s" : ""}",
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
@@ -98,7 +101,7 @@ class OrderUI extends HookConsumerWidget {
),
const SizedBox(height: 3),
Text(
- uiCollectionSlotToString(order.collectionSlot),
+ uiCollectionSlotToString(order.collectionSlot, context),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
@@ -132,9 +135,17 @@ class OrderUI extends HookConsumerWidget {
await showDialog(
context: context,
builder: ((context) => CustomDialogBox(
- title: AMAPTextConstants.delete,
- descriptions: AMAPTextConstants.deletingOrder,
+ title: AppLocalizations.of(context)!.amapDelete,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.amapDeletingOrder,
onYes: () async {
+ final deletedOrderMsg = AppLocalizations.of(
+ context,
+ )!.amapDeletedOrder;
+ final deletingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapDeletingError;
await tokenExpireWrapper(ref, () async {
orderListNotifier.deleteOrder(order).then((
value,
@@ -143,12 +154,12 @@ class OrderUI extends HookConsumerWidget {
balanceNotifier.updateCash(order.amount);
displayToastWithContext(
TypeMsg.msg,
- AMAPTextConstants.deletedOrder,
+ deletedOrderMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- AMAPTextConstants.deletingError,
+ deletingErrorMsg,
);
}
});
@@ -171,9 +182,9 @@ class OrderUI extends HookConsumerWidget {
),
],
)
- : const Text(
- AMAPTextConstants.locked,
- style: TextStyle(
+ : Text(
+ AppLocalizations.of(context)!.amapLocked,
+ style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
color: AMAPColorConstants.textDark,
diff --git a/lib/amap/ui/components/product_ui.dart b/lib/amap/ui/components/product_ui.dart
index 3c3ef3c323..68d2901568 100644
--- a/lib/amap/ui/components/product_ui.dart
+++ b/lib/amap/ui/components/product_ui.dart
@@ -6,6 +6,7 @@ import 'package:titan/amap/tools/constants.dart';
import 'package:titan/tools/ui/layouts/card_button.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ProductCard extends StatelessWidget {
final Product product;
@@ -103,7 +104,7 @@ class ProductCard extends StatelessWidget {
: Container(
margin: const EdgeInsets.only(bottom: 5),
child: Text(
- "${AMAPTextConstants.quantity} : ${product.quantity}",
+ "${AppLocalizations.of(context)!.amapQuantity} : ${product.quantity}",
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
diff --git a/lib/amap/ui/pages/admin_page/account_handler.dart b/lib/amap/ui/pages/admin_page/account_handler.dart
index 3f20ade3d5..3a4b9dff46 100644
--- a/lib/amap/ui/pages/admin_page/account_handler.dart
+++ b/lib/amap/ui/pages/admin_page/account_handler.dart
@@ -11,6 +11,7 @@ import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:titan/tools/ui/widgets/styled_search_bar.dart';
import 'package:titan/user/providers/user_list_provider.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AccountHandler extends HookConsumerWidget {
const AccountHandler({super.key});
@@ -28,7 +29,7 @@ class AccountHandler extends HookConsumerWidget {
return Column(
children: [
StyledSearchBar(
- label: AMAPTextConstants.accounts,
+ label: AppLocalizations.of(context)!.amapAccounts,
color: AMAPColorConstants.textDark,
onChanged: (value) async {
if (!searchingAmapUser) {
diff --git a/lib/amap/ui/pages/admin_page/admin_page.dart b/lib/amap/ui/pages/admin_page/admin_page.dart
index 328c3e652d..a7910e6fef 100644
--- a/lib/amap/ui/pages/admin_page/admin_page.dart
+++ b/lib/amap/ui/pages/admin_page/admin_page.dart
@@ -19,6 +19,7 @@ class AdminPage extends HookConsumerWidget {
final productListNotifier = ref.read(productListProvider.notifier);
return AmapTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await cashNotifier.loadCashList();
await deliveryListNotifier.loadDeliveriesList();
diff --git a/lib/amap/ui/pages/admin_page/delivery_handler.dart b/lib/amap/ui/pages/admin_page/delivery_handler.dart
index b500fabeba..60be021992 100644
--- a/lib/amap/ui/pages/admin_page/delivery_handler.dart
+++ b/lib/amap/ui/pages/admin_page/delivery_handler.dart
@@ -13,6 +13,7 @@ import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DeliveryHandler extends HookConsumerWidget {
const DeliveryHandler({super.key});
@@ -24,9 +25,9 @@ class DeliveryHandler extends HookConsumerWidget {
final selectedNotifier = ref.watch(selectedListProvider.notifier);
return Column(
children: [
- const AlignLeftText(
- AMAPTextConstants.deliveries,
- padding: EdgeInsets.symmetric(horizontal: 30),
+ AlignLeftText(
+ AppLocalizations.of(context)!.amapDeliveries,
+ padding: const EdgeInsets.symmetric(horizontal: 30),
color: AMAPColorConstants.textDark,
),
const SizedBox(height: 10),
diff --git a/lib/amap/ui/pages/admin_page/delivery_ui.dart b/lib/amap/ui/pages/admin_page/delivery_ui.dart
index 809115ea2b..38f0da2991 100644
--- a/lib/amap/ui/pages/admin_page/delivery_ui.dart
+++ b/lib/amap/ui/pages/admin_page/delivery_ui.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/amap/class/delivery.dart';
import 'package:titan/amap/providers/delivery_id_provider.dart';
import 'package:titan/amap/providers/delivery_list_provider.dart';
@@ -19,6 +20,7 @@ import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DeliveryUi extends HookConsumerWidget {
final Delivery delivery;
@@ -26,6 +28,7 @@ class DeliveryUi extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final deliveryIdNotifier = ref.watch(deliveryIdProvider.notifier);
final deliveryListNotifier = ref.watch(deliveryListProvider.notifier);
final deliveryOrders = ref.watch(
@@ -67,7 +70,7 @@ class DeliveryUi extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- '${AMAPTextConstants.the} ${processDate(delivery.deliveryDate)}',
+ '${AppLocalizations.of(context)!.amapThe} ${DateFormat.yMd(locale).format(delivery.deliveryDate)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -102,8 +105,8 @@ class DeliveryUi extends HookConsumerWidget {
dataBuilder: (context, orders) {
return Text(
orders.isEmpty
- ? AMAPTextConstants.noCurrentOrder
- : '${orders.length} ${AMAPTextConstants.oneOrder}${orders.length != 1 ? "s" : ""}',
+ ? AppLocalizations.of(context)!.amapNoCurrentOrder
+ : '${orders.length} ${AppLocalizations.of(context)!.amapOneOrder}${orders.length != 1 ? "s" : ""}',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
@@ -113,7 +116,7 @@ class DeliveryUi extends HookConsumerWidget {
},
),
Text(
- "${delivery.products.length} ${AMAPTextConstants.product}${delivery.products.length != 1 ? "s" : ""}",
+ "${delivery.products.length} ${AppLocalizations.of(context)!.amapProduct}${delivery.products.length != 1 ? "s" : ""}",
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
@@ -177,10 +180,19 @@ class DeliveryUi extends HookConsumerWidget {
await showDialog(
context: context,
builder: ((context) => CustomDialogBox(
- title: AMAPTextConstants.deleteDelivery,
- descriptions:
- AMAPTextConstants.deleteDeliveryDescription,
+ title: AppLocalizations.of(
+ context,
+ )!.amapDeleteDelivery,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.amapDeleteDeliveryDescription,
onYes: () async {
+ final deletedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeletedDelivery;
+ final deletingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapDeletingError;
await tokenExpireWrapper(ref, () async {
deliveryListNotifier
.deleteDelivery(delivery)
@@ -188,12 +200,12 @@ class DeliveryUi extends HookConsumerWidget {
if (value) {
displayVoteWithContext(
TypeMsg.msg,
- AMAPTextConstants.deletedDelivery,
+ deletedDeliveryMsg,
);
} else {
displayVoteWithContext(
TypeMsg.error,
- AMAPTextConstants.deletingError,
+ deletingErrorMsg,
);
}
});
@@ -221,20 +233,48 @@ class DeliveryUi extends HookConsumerWidget {
context: context,
builder: ((context) => CustomDialogBox(
title: delivery.status == DeliveryStatus.creation
- ? AMAPTextConstants.openDelivery
+ ? AppLocalizations.of(context)!.amapOpenDelivery
: delivery.status == DeliveryStatus.available
- ? AMAPTextConstants.lock
+ ? AppLocalizations.of(context)!.amapLock
: delivery.status == DeliveryStatus.locked
- ? AMAPTextConstants.deliver
- : AMAPTextConstants.archive,
+ ? AppLocalizations.of(context)!.amapDeliver
+ : AppLocalizations.of(context)!.amapArchive,
descriptions: delivery.status == DeliveryStatus.creation
- ? AMAPTextConstants.openningDelivery
+ ? AppLocalizations.of(context)!.amapOpenningDelivery
: delivery.status == DeliveryStatus.available
- ? AMAPTextConstants.lockingDelivery
+ ? AppLocalizations.of(context)!.amapLockingDelivery
: delivery.status == DeliveryStatus.locked
- ? AMAPTextConstants.deliveringDelivery
- : AMAPTextConstants.archivingDelivery,
+ ? AppLocalizations.of(
+ context,
+ )!.amapDeliveringDelivery
+ : AppLocalizations.of(
+ context,
+ )!.amapArchivingDelivery,
onYes: () async {
+ final openedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryOpened;
+ final notOpenedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryNotOpened;
+ final lockedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryLocked;
+ final notLockedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryNotLocked;
+ final deliveredDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryDelivered;
+ final notDeliveredDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryNotDelivered;
+ final archivedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryArchived;
+ final notArchivedDeliveryMsg = AppLocalizations.of(
+ context,
+ )!.amapDeliveryNotArchived;
await tokenExpireWrapper(ref, () async {
switch (delivery.status) {
case DeliveryStatus.creation:
@@ -243,12 +283,12 @@ class DeliveryUi extends HookConsumerWidget {
if (value) {
displayVoteWithContext(
TypeMsg.msg,
- AMAPTextConstants.deliveryOpened,
+ openedDeliveryMsg,
);
} else {
displayVoteWithContext(
TypeMsg.error,
- AMAPTextConstants.deliveryNotOpened,
+ notOpenedDeliveryMsg,
);
}
break;
@@ -258,12 +298,12 @@ class DeliveryUi extends HookConsumerWidget {
if (value) {
displayVoteWithContext(
TypeMsg.msg,
- AMAPTextConstants.deliveryLocked,
+ lockedDeliveryMsg,
);
} else {
displayVoteWithContext(
TypeMsg.error,
- AMAPTextConstants.deliveryNotLocked,
+ notLockedDeliveryMsg,
);
}
break;
@@ -273,12 +313,12 @@ class DeliveryUi extends HookConsumerWidget {
if (value) {
displayVoteWithContext(
TypeMsg.msg,
- AMAPTextConstants.deliveryDelivered,
+ deliveredDeliveryMsg,
);
} else {
displayVoteWithContext(
TypeMsg.error,
- AMAPTextConstants.deliveryNotDelivered,
+ notDeliveredDeliveryMsg,
);
}
break;
@@ -288,12 +328,12 @@ class DeliveryUi extends HookConsumerWidget {
if (value) {
displayVoteWithContext(
TypeMsg.msg,
- AMAPTextConstants.deliveryArchived,
+ archivedDeliveryMsg,
);
} else {
displayVoteWithContext(
TypeMsg.error,
- AMAPTextConstants.deliveryNotArchived,
+ notArchivedDeliveryMsg,
);
}
break;
@@ -345,12 +385,14 @@ class DeliveryUi extends HookConsumerWidget {
padding: const EdgeInsets.only(bottom: 2),
child: Text(
delivery.status == DeliveryStatus.creation
- ? AMAPTextConstants.openDelivery
+ ? AppLocalizations.of(context)!.amapOpenDelivery
: delivery.status == DeliveryStatus.available
- ? AMAPTextConstants.closeDelivery
+ ? AppLocalizations.of(context)!.amapCloseDelivery
: delivery.status == DeliveryStatus.locked
- ? AMAPTextConstants.endingDelivery
- : AMAPTextConstants.archiveDelivery,
+ ? AppLocalizations.of(context)!.amapEndingDelivery
+ : AppLocalizations.of(
+ context,
+ )!.amapArchiveDelivery,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
diff --git a/lib/amap/ui/pages/admin_page/product_handler.dart b/lib/amap/ui/pages/admin_page/product_handler.dart
index 57abe7c04f..90b89117bb 100644
--- a/lib/amap/ui/pages/admin_page/product_handler.dart
+++ b/lib/amap/ui/pages/admin_page/product_handler.dart
@@ -15,6 +15,7 @@ import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ProductHandler extends HookConsumerWidget {
const ProductHandler({super.key});
@@ -37,9 +38,9 @@ class ProductHandler extends HookConsumerWidget {
return Column(
children: [
- const AlignLeftText(
- padding: EdgeInsets.symmetric(horizontal: 30),
- AMAPTextConstants.products,
+ AlignLeftText(
+ padding: const EdgeInsets.symmetric(horizontal: 30),
+ AppLocalizations.of(context)!.amapProducts,
color: AMAPColorConstants.textDark,
),
const SizedBox(height: 10),
@@ -74,7 +75,9 @@ class ProductHandler extends HookConsumerWidget {
),
),
products.isEmpty
- ? const Center(child: Text(AMAPTextConstants.noProduct))
+ ? Center(
+ child: Text(AppLocalizations.of(context)!.amapNoProduct),
+ )
: Row(
children: products
.map(
@@ -84,22 +87,33 @@ class ProductHandler extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => CustomDialogBox(
- title: AMAPTextConstants.deleteProduct,
- descriptions: AMAPTextConstants
- .deleteProductDescription,
+ title: AppLocalizations.of(
+ context,
+ )!.amapDeleteProduct,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.amapDeleteProductDescription,
onYes: () {
+ final deletedProductMsg =
+ AppLocalizations.of(
+ context,
+ )!.amapDeletedProduct;
+ final deletingErrorMsg =
+ AppLocalizations.of(
+ context,
+ )!.amapDeletingError;
tokenExpireWrapper(ref, () async {
final value = await productsNotifier
.deleteProduct(e);
if (value) {
displayToastWithContext(
TypeMsg.msg,
- AMAPTextConstants.deletedProduct,
+ deletedProductMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- AMAPTextConstants.productInDelivery,
+ deletingErrorMsg,
);
}
});
diff --git a/lib/amap/ui/pages/admin_page/user_cash_ui.dart b/lib/amap/ui/pages/admin_page/user_cash_ui.dart
index 898f85f058..0f7f7a48e6 100644
--- a/lib/amap/ui/pages/admin_page/user_cash_ui.dart
+++ b/lib/amap/ui/pages/admin_page/user_cash_ui.dart
@@ -13,6 +13,7 @@ import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/tools/ui/widgets/text_entry.dart';
+import 'package:titan/l10n/app_localizations.dart';
class UserCashUi extends HookConsumerWidget {
final Cash cash;
@@ -150,6 +151,12 @@ class UserCashUi extends HookConsumerWidget {
if (key.currentState == null) {
return;
}
+ final updatedAmountMsg = AppLocalizations.of(
+ context,
+ )!.amapUpdatedAmount;
+ final updatingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapUpdatingError;
if (key.currentState!.validate()) {
await tokenExpireWrapper(ref, () async {
await ref
@@ -168,12 +175,12 @@ class UserCashUi extends HookConsumerWidget {
toggle();
displayVoteWithContext(
TypeMsg.msg,
- AMAPTextConstants.updatedAmount,
+ updatedAmountMsg,
);
} else {
displayVoteWithContext(
TypeMsg.error,
- AMAPTextConstants.updatingError,
+ updatingErrorMsg,
);
}
});
diff --git a/lib/amap/ui/pages/delivery_pages/add_edit_delivery_cmd_page.dart b/lib/amap/ui/pages/delivery_pages/add_edit_delivery_cmd_page.dart
index a7a01c23ba..8c8a08ef01 100644
--- a/lib/amap/ui/pages/delivery_pages/add_edit_delivery_cmd_page.dart
+++ b/lib/amap/ui/pages/delivery_pages/add_edit_delivery_cmd_page.dart
@@ -1,6 +1,7 @@
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
import 'package:titan/amap/class/delivery.dart';
import 'package:titan/amap/providers/delivery_list_provider.dart';
import 'package:titan/amap/providers/delivery_order_list_provider.dart';
@@ -19,17 +20,19 @@ import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/widgets/date_entry.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditDeliveryPage extends HookConsumerWidget {
const AddEditDeliveryPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final formKey = GlobalKey();
final delivery = ref.watch(deliveryProvider);
final isEdit = delivery.id != Delivery.empty().id;
final dateController = useTextEditingController(
- text: isEdit ? processDate(delivery.deliveryDate) : '',
+ text: isEdit ? DateFormat.yMd(locale).format(delivery.deliveryDate) : '',
);
final productList = ref.watch(productListProvider);
final sortedProductsList = ref.watch(sortedByCategoryProductsProvider);
@@ -55,23 +58,23 @@ class AddEditDeliveryPage extends HookConsumerWidget {
child: Column(
children: [
const SizedBox(height: 20),
- const AlignLeftText(
- AMAPTextConstants.addDelivery,
+ AlignLeftText(
+ AppLocalizations.of(context)!.amapAddDelivery,
color: AMAPColorConstants.green2,
),
Container(
margin: const EdgeInsets.symmetric(vertical: 30),
child: DateEntry(
onTap: () => getOnlyDayDate(context, dateController),
- label: AMAPTextConstants.commandDate,
+ label: AppLocalizations.of(context)!.amapCommandDate,
controller: dateController,
enabledColor: AMAPColorConstants.enabled,
color: AMAPColorConstants.greenGradient2,
),
),
const SizedBox(height: 15),
- const AlignLeftText(
- AMAPTextConstants.commandProducts,
+ AlignLeftText(
+ AppLocalizations.of(context)!.amapCommandProducts,
fontSize: 25,
),
const SizedBox(height: 35),
@@ -140,7 +143,7 @@ class AddEditDeliveryPage extends HookConsumerWidget {
)
.toList(),
deliveryDate: DateTime.parse(
- processDateBack(date),
+ processDateBack(date, locale.toString()),
),
status: DeliveryStatus.creation,
);
@@ -148,6 +151,19 @@ class AddEditDeliveryPage extends HookConsumerWidget {
final deliveryNotifier = ref.watch(
deliveryListProvider.notifier,
);
+ final editedCommandMsg = AppLocalizations.of(
+ context,
+ )!.amapEditedCommand;
+ final addedCommandMsg = AppLocalizations.of(
+ context,
+ )!.amapAddedCommand;
+ final editingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapEditingError;
+ final alreadyExistCommandMsg =
+ AppLocalizations.of(
+ context,
+ )!.amapAlreadyExistCommand;
final value = isEdit
? await deliveryNotifier.updateDelivery(
del,
@@ -158,7 +174,7 @@ class AddEditDeliveryPage extends HookConsumerWidget {
if (isEdit) {
displayToastWithContext(
TypeMsg.msg,
- AMAPTextConstants.editedCommand,
+ editedCommandMsg,
);
} else {
final deliveryOrdersNotifier = ref.watch(
@@ -174,19 +190,19 @@ class AddEditDeliveryPage extends HookConsumerWidget {
});
displayToastWithContext(
TypeMsg.msg,
- AMAPTextConstants.addedCommand,
+ addedCommandMsg,
);
}
} else {
if (isEdit) {
displayToastWithContext(
TypeMsg.error,
- AMAPTextConstants.editingError,
+ editingErrorMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- AMAPTextConstants.alreadyExistCommand,
+ alreadyExistCommandMsg,
);
}
}
@@ -195,14 +211,18 @@ class AddEditDeliveryPage extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- AMAPTextConstants.addingError,
+ AppLocalizations.of(context)!.amapAddingError,
);
}
},
child: Text(
isEdit
- ? AMAPTextConstants.editDelivery
- : AMAPTextConstants.addDelivery,
+ ? AppLocalizations.of(
+ context,
+ )!.amapEditDelivery
+ : AppLocalizations.of(
+ context,
+ )!.amapAddDelivery,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
diff --git a/lib/amap/ui/pages/detail_delivery_page/detail_page.dart b/lib/amap/ui/pages/detail_delivery_page/detail_page.dart
index b84ed8b469..6aef738a04 100644
--- a/lib/amap/ui/pages/detail_delivery_page/detail_page.dart
+++ b/lib/amap/ui/pages/detail_delivery_page/detail_page.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/amap/class/order.dart';
import 'package:titan/amap/class/product.dart';
import 'package:titan/amap/providers/cash_list_provider.dart';
@@ -12,17 +13,18 @@ import 'package:titan/amap/tools/constants.dart';
import 'package:titan/amap/ui/amap.dart';
import 'package:titan/amap/ui/pages/detail_delivery_page/order_detail_ui.dart';
import 'package:titan/amap/ui/pages/detail_delivery_page/product_detail_ui.dart';
-import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/widgets/align_left_text.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/widgets/loader.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DetailDeliveryPage extends HookConsumerWidget {
const DetailDeliveryPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final delivery = ref.watch(deliveryProvider);
final deliveryOrders = ref.watch(adminDeliveryOrderListProvider);
final orders = deliveryOrders[delivery.id];
@@ -36,6 +38,7 @@ class DetailDeliveryPage extends HookConsumerWidget {
final cash = ref.watch(cashListProvider);
return AmapTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await deliveryProductListNotifier.loadProductList(delivery.products);
await deliveryListNotifier.loadDeliveriesList();
@@ -47,15 +50,15 @@ class DetailDeliveryPage extends HookConsumerWidget {
child: Column(
children: [
Text(
- "${AMAPTextConstants.deliveryDate} : ${processDate(delivery.deliveryDate)}",
+ "${AppLocalizations.of(context)!.amapDeliveryDate} : ${DateFormat.yMd(locale).format(delivery.deliveryDate)}",
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
- const AlignLeftText(
- "${AMAPTextConstants.products} :",
+ AlignLeftText(
+ "${AppLocalizations.of(context)!.amapProducts} :",
color: AMAPColorConstants.textDark,
),
],
@@ -111,9 +114,9 @@ class DetailDeliveryPage extends HookConsumerWidget {
);
}).values,
const SizedBox(height: 20),
- const AlignLeftText(
- "${AMAPTextConstants.orders} :",
- padding: EdgeInsets.only(left: 30),
+ AlignLeftText(
+ "${AppLocalizations.of(context)!.amapOrders} :",
+ padding: const EdgeInsets.only(left: 30),
color: AMAPColorConstants.textDark,
),
const SizedBox(height: 30),
@@ -128,8 +131,10 @@ class DetailDeliveryPage extends HookConsumerWidget {
if (data.isEmpty) {
return Container(
margin: const EdgeInsets.only(bottom: 50),
- child: const Center(
- child: Text(AMAPTextConstants.noOrder),
+ child: Center(
+ child: Text(
+ AppLocalizations.of(context)!.amapNoOrder,
+ ),
),
);
}
diff --git a/lib/amap/ui/pages/detail_delivery_page/order_detail_ui.dart b/lib/amap/ui/pages/detail_delivery_page/order_detail_ui.dart
index 73963f39f3..0c8065e7f5 100644
--- a/lib/amap/ui/pages/detail_delivery_page/order_detail_ui.dart
+++ b/lib/amap/ui/pages/detail_delivery_page/order_detail_ui.dart
@@ -14,6 +14,7 @@ import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DetailOrderUI extends HookConsumerWidget {
final Order order;
@@ -105,7 +106,7 @@ class DetailOrderUI extends HookConsumerWidget {
Row(
children: [
Text(
- "${order.products.fold(0, (value, product) => value + product.quantity)} ${AMAPTextConstants.product}${order.products.fold(0, (value, product) => value + product.quantity) != 1 ? "s" : ""}",
+ "${order.products.fold(0, (value, product) => value + product.quantity)} ${AppLocalizations.of(context)!.amapProduct}${order.products.fold(0, (value, product) => value + product.quantity) != 1 ? "s" : ""}",
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
@@ -127,7 +128,7 @@ class DetailOrderUI extends HookConsumerWidget {
Row(
children: [
Text(
- "${AMAPTextConstants.amount} : ${userCash.balance.toStringAsFixed(2)}€",
+ "${AppLocalizations.of(context)!.amapAmount} : ${userCash.balance.toStringAsFixed(2)}€",
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
@@ -140,9 +141,17 @@ class DetailOrderUI extends HookConsumerWidget {
await showDialog(
context: context,
builder: ((context) => CustomDialogBox(
- title: AMAPTextConstants.delete,
- descriptions: AMAPTextConstants.deletingOrder,
+ title: AppLocalizations.of(context)!.amapDelete,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.amapDeletingOrder,
onYes: () async {
+ final deletedOrderMsg = AppLocalizations.of(
+ context,
+ )!.amapDeletedOrder;
+ final deletingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapDeletingError;
await tokenExpireWrapper(ref, () async {
final index = orderList.maybeWhen(
data: (data) => data.indexWhere(
@@ -167,12 +176,12 @@ class DetailOrderUI extends HookConsumerWidget {
);
displayToastWithContext(
TypeMsg.msg,
- AMAPTextConstants.deletedOrder,
+ deletedOrderMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- AMAPTextConstants.deletingError,
+ deletingErrorMsg,
);
}
});
diff --git a/lib/amap/ui/pages/detail_delivery_page/product_detail_ui.dart b/lib/amap/ui/pages/detail_delivery_page/product_detail_ui.dart
index 08db18c5d5..6376a197b3 100644
--- a/lib/amap/ui/pages/detail_delivery_page/product_detail_ui.dart
+++ b/lib/amap/ui/pages/detail_delivery_page/product_detail_ui.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:titan/amap/class/product.dart';
import 'package:titan/amap/tools/constants.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ProductDetailCard extends StatelessWidget {
final Product product;
@@ -41,7 +42,7 @@ class ProductDetailCard extends StatelessWidget {
),
const SizedBox(height: 4),
AutoSizeText(
- "${AMAPTextConstants.quantity} : $quantity",
+ "${AppLocalizations.of(context)!.amapQuantity} : $quantity",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
diff --git a/lib/amap/ui/pages/detail_page/detail_page.dart b/lib/amap/ui/pages/detail_page/detail_page.dart
index cdb4009ade..3e204b7226 100644
--- a/lib/amap/ui/pages/detail_page/detail_page.dart
+++ b/lib/amap/ui/pages/detail_page/detail_page.dart
@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/amap/providers/order_provider.dart';
-import 'package:titan/amap/tools/constants.dart';
import 'package:titan/amap/ui/amap.dart';
import 'package:titan/amap/ui/components/order_ui.dart';
import 'package:titan/amap/ui/components/product_ui.dart';
import 'package:titan/tools/ui/widgets/align_left_text.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DetailPage extends HookConsumerWidget {
const DetailPage({super.key});
@@ -39,9 +39,9 @@ class DetailPage extends HookConsumerWidget {
child: Column(
children: [
const SizedBox(height: 50),
- const AlignLeftText(
- padding: EdgeInsets.symmetric(horizontal: 20),
- AMAPTextConstants.products,
+ AlignLeftText(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ AppLocalizations.of(context)!.amapProducts,
fontSize: 25,
),
const SizedBox(height: 10),
diff --git a/lib/amap/ui/pages/list_products_page/category_page.dart b/lib/amap/ui/pages/list_products_page/category_page.dart
index a390f1472e..9afb0a22cf 100644
--- a/lib/amap/ui/pages/list_products_page/category_page.dart
+++ b/lib/amap/ui/pages/list_products_page/category_page.dart
@@ -10,6 +10,7 @@ import 'package:titan/amap/tools/constants.dart';
import 'package:titan/amap/ui/pages/list_products_page/product_ui_list.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/widgets/align_left_text.dart';
+import 'package:titan/l10n/app_localizations.dart';
class CategoryPage extends HookConsumerWidget {
final String category;
@@ -160,7 +161,7 @@ class CategoryPage extends HookConsumerWidget {
color: AMAPColorConstants.background,
),
Text(
- AMAPTextConstants.seeMore,
+ AppLocalizations.of(context)!.amapSeeMore,
style: TextStyle(
fontSize: 18,
color: AMAPColorConstants.background,
diff --git a/lib/amap/ui/pages/list_products_page/list_products.dart b/lib/amap/ui/pages/list_products_page/list_products.dart
index e2829189e6..7d831a439f 100644
--- a/lib/amap/ui/pages/list_products_page/list_products.dart
+++ b/lib/amap/ui/pages/list_products_page/list_products.dart
@@ -6,10 +6,10 @@ import 'package:titan/amap/providers/scroll_provider.dart';
import 'package:titan/amap/providers/sorted_delivery_product.dart';
import 'package:titan/amap/providers/page_controller_provider.dart';
import 'package:titan/amap/providers/scroll_controller_provider.dart';
-import 'package:titan/amap/tools/constants.dart';
import 'package:titan/amap/ui/pages/list_products_page/category_page.dart';
import 'package:titan/amap/ui/pages/list_products_page/web_page_navigation_button.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/providers/is_web_format_provider.dart';
class ListProducts extends HookConsumerWidget {
const ListProducts({super.key});
@@ -48,10 +48,10 @@ class ListProducts extends HookConsumerWidget {
physics: const BouncingScrollPhysics(),
children: sortedDeliveryProductsList.isEmpty
? [
- const Center(
+ Center(
child: Text(
- AMAPTextConstants.noProduct,
- style: TextStyle(
+ AppLocalizations.of(context)!.amapNoProduct,
+ style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
diff --git a/lib/amap/ui/pages/list_products_page/product_choice_button.dart b/lib/amap/ui/pages/list_products_page/product_choice_button.dart
index 789ba76192..07fd6634da 100644
--- a/lib/amap/ui/pages/list_products_page/product_choice_button.dart
+++ b/lib/amap/ui/pages/list_products_page/product_choice_button.dart
@@ -13,6 +13,7 @@ import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/user/providers/user_provider.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ProductChoiceButton extends HookConsumerWidget {
const ProductChoiceButton({super.key});
@@ -69,7 +70,7 @@ class ProductChoiceButton extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- AMAPTextConstants.noProduct,
+ AppLocalizations.of(context)!.amapNoProduct,
);
} else {
Order newOrder = order.copyWith(
@@ -78,6 +79,18 @@ class ProductChoiceButton extends HookConsumerWidget {
lastAmount: order.amount,
);
await tokenExpireWrapper(ref, () async {
+ final updatedOrderMsg = AppLocalizations.of(
+ context,
+ )!.amapUpdatedOrder;
+ final addedOrderMsg = AppLocalizations.of(
+ context,
+ )!.amapAddedOrder;
+ final updatingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapUpdatingError;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.amapAddingError;
final value = isEdit
? await orderListNotifier.updateOrder(newOrder)
: await orderListNotifier.addOrder(newOrder);
@@ -87,34 +100,25 @@ class ProductChoiceButton extends HookConsumerWidget {
order.lastAmount - order.amount,
);
if (isEdit) {
- displayToastWithContext(
- TypeMsg.msg,
- AMAPTextConstants.updatedOrder,
- );
+ displayToastWithContext(TypeMsg.msg, updatedOrderMsg);
} else {
- displayToastWithContext(
- TypeMsg.msg,
- AMAPTextConstants.addedOrder,
- );
+ displayToastWithContext(TypeMsg.msg, addedOrderMsg);
}
} else {
if (isEdit) {
displayToastWithContext(
TypeMsg.error,
- AMAPTextConstants.updatingError,
+ updatingErrorMsg,
);
} else {
- displayToastWithContext(
- TypeMsg.error,
- AMAPTextConstants.addingError,
- );
+ displayToastWithContext(TypeMsg.error, addingErrorMsg);
}
}
});
}
},
child: Text(
- "${AMAPTextConstants.confirm} (${order.amount.toStringAsFixed(2)}€)",
+ "${AppLocalizations.of(context)!.amapConfirm} (${order.amount.toStringAsFixed(2)}€)",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
@@ -159,8 +163,10 @@ class ProductChoiceButton extends HookConsumerWidget {
showDialog(
context: context,
builder: (BuildContext context) => CustomDialogBox(
- descriptions: AMAPTextConstants.deletingOrder,
- title: AMAPTextConstants.deleting,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.amapDeletingOrder,
+ title: AppLocalizations.of(context)!.amapDeleting,
onYes: () {
orderNotifier.setOrder(Order.empty());
QR.back();
diff --git a/lib/amap/ui/pages/main_page/collection_slot_selector.dart b/lib/amap/ui/pages/main_page/collection_slot_selector.dart
index 9de132519b..3a541075da 100644
--- a/lib/amap/ui/pages/main_page/collection_slot_selector.dart
+++ b/lib/amap/ui/pages/main_page/collection_slot_selector.dart
@@ -38,7 +38,7 @@ class CollectionSlotSelector extends HookConsumerWidget {
),
child: Center(
child: Text(
- capitalize(uiCollectionSlotToString(collectionSlot)),
+ capitalize(uiCollectionSlotToString(collectionSlot, context)),
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
diff --git a/lib/amap/ui/pages/main_page/delivery_section.dart b/lib/amap/ui/pages/main_page/delivery_section.dart
index 65aba3ecfd..0921d0010e 100644
--- a/lib/amap/ui/pages/main_page/delivery_section.dart
+++ b/lib/amap/ui/pages/main_page/delivery_section.dart
@@ -7,6 +7,7 @@ import 'package:titan/amap/tools/constants.dart';
import 'package:titan/amap/ui/pages/main_page/delivery_ui.dart';
import 'package:titan/tools/ui/widgets/align_left_text.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DeliverySection extends HookConsumerWidget {
final bool showSelected;
@@ -30,7 +31,7 @@ class DeliverySection extends HookConsumerWidget {
return Column(
children: [
AlignLeftText(
- AMAPTextConstants.deliveries,
+ AppLocalizations.of(context)!.amapDeliveries,
padding: const EdgeInsets.symmetric(horizontal: 30),
color: showSelected ? Colors.white : AMAPColorConstants.textDark,
),
@@ -38,8 +39,10 @@ class DeliverySection extends HookConsumerWidget {
value: deliveries,
builder: (context, data) {
if (availableDeliveries.isEmpty) {
- return const Center(
- child: Text(AMAPTextConstants.notPlannedDelivery),
+ return Center(
+ child: Text(
+ AppLocalizations.of(context)!.amapNotPlannedDelivery,
+ ),
);
}
return SingleChildScrollView(
diff --git a/lib/amap/ui/pages/main_page/delivery_ui.dart b/lib/amap/ui/pages/main_page/delivery_ui.dart
index 14d82a026e..903e3eb26a 100644
--- a/lib/amap/ui/pages/main_page/delivery_ui.dart
+++ b/lib/amap/ui/pages/main_page/delivery_ui.dart
@@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/amap/class/delivery.dart';
import 'package:titan/amap/providers/delivery_provider.dart';
import 'package:titan/amap/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DeliveryUi extends HookConsumerWidget {
final Delivery delivery;
@@ -18,6 +19,7 @@ class DeliveryUi extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final selectedDelivery = ref.watch(deliveryProvider);
final selected = selectedDelivery.id == delivery.id;
return GestureDetector(
@@ -55,7 +57,7 @@ class DeliveryUi extends HookConsumerWidget {
children: [
const SizedBox(width: 10),
Text(
- '${AMAPTextConstants.the} ${processDate(delivery.deliveryDate)}',
+ '${AppLocalizations.of(context)!.amapThe} ${DateFormat.yMd(locale).format(delivery.deliveryDate)}',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -66,7 +68,7 @@ class DeliveryUi extends HookConsumerWidget {
),
const Spacer(),
Text(
- "${delivery.products.length} ${AMAPTextConstants.product}${delivery.products.length != 1 ? "s" : ""}",
+ "${delivery.products.length} ${AppLocalizations.of(context)!.amapProduct}${delivery.products.length != 1 ? "s" : ""}",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
diff --git a/lib/amap/ui/pages/main_page/main_page.dart b/lib/amap/ui/pages/main_page/main_page.dart
index 1d5c673abf..3f38eb0bee 100644
--- a/lib/amap/ui/pages/main_page/main_page.dart
+++ b/lib/amap/ui/pages/main_page/main_page.dart
@@ -26,6 +26,7 @@ import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/user/providers/user_provider.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AmapMainPage extends HookConsumerWidget {
const AmapMainPage({super.key});
@@ -65,6 +66,7 @@ class AmapMainPage extends HookConsumerWidget {
return AmapTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await ordersNotifier.loadOrderList(me.id);
await balanceNotifier.loadCashByUser(me.id);
@@ -83,7 +85,7 @@ class AmapMainPage extends HookConsumerWidget {
child: AsyncChild(
value: balance,
builder: (context, s) => Text(
- "${AMAPTextConstants.amount} : ${s.balance.toStringAsFixed(2)}€",
+ "${AppLocalizations.of(context)!.amapAmount} : ${s.balance.toStringAsFixed(2)}€",
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@@ -181,8 +183,8 @@ class AmapMainPage extends HookConsumerWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- const AlignLeftText(
- AMAPTextConstants.addOrder,
+ AlignLeftText(
+ AppLocalizations.of(context)!.amapAddOrder,
color: Colors.white,
),
IconButton(
@@ -234,7 +236,9 @@ class AmapMainPage extends HookConsumerWidget {
} else {
displayToastWithoutContext(
TypeMsg.error,
- AMAPTextConstants.noSelectedDelivery,
+ AppLocalizations.of(
+ context,
+ )!.amapNoSelectedDelivery,
);
}
},
@@ -267,10 +271,10 @@ class AmapMainPage extends HookConsumerWidget {
child: Container(
padding: const EdgeInsets.only(bottom: 5),
width: double.infinity,
- child: const Center(
+ child: Center(
child: Text(
- AMAPTextConstants.nextStep,
- style: TextStyle(
+ AppLocalizations.of(context)!.amapNextStep,
+ style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.w900,
color: Colors.white,
diff --git a/lib/amap/ui/pages/main_page/orders_section.dart b/lib/amap/ui/pages/main_page/orders_section.dart
index bbfca64b5d..f59c684f73 100644
--- a/lib/amap/ui/pages/main_page/orders_section.dart
+++ b/lib/amap/ui/pages/main_page/orders_section.dart
@@ -13,6 +13,7 @@ import 'package:titan/tools/ui/widgets/align_left_text.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
+import 'package:titan/l10n/app_localizations.dart';
class OrderSection extends HookConsumerWidget {
final VoidCallback onTap, addOrder, onEdit;
@@ -38,9 +39,9 @@ class OrderSection extends HookConsumerWidget {
return Column(
children: [
- const AlignLeftText(
- AMAPTextConstants.orders,
- padding: EdgeInsets.symmetric(horizontal: 30),
+ AlignLeftText(
+ AppLocalizations.of(context)!.amapOrders,
+ padding: const EdgeInsets.symmetric(horizontal: 30),
color: AMAPColorConstants.textDark,
),
const SizedBox(height: 10),
diff --git a/lib/amap/ui/pages/presentation_page/text.dart b/lib/amap/ui/pages/presentation_page/text.dart
index 16caacedf2..6933662d84 100644
--- a/lib/amap/ui/pages/presentation_page/text.dart
+++ b/lib/amap/ui/pages/presentation_page/text.dart
@@ -7,6 +7,7 @@ import 'package:titan/amap/ui/amap.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:url_launcher/url_launcher.dart';
+import 'package:titan/l10n/app_localizations.dart';
class PresentationPage extends HookConsumerWidget {
const PresentationPage({super.key});
@@ -31,7 +32,7 @@ class PresentationPage extends HookConsumerWidget {
text: TextSpan(
children: [
TextSpan(
- text: AMAPTextConstants.presentation1,
+ text: AppLocalizations.of(context)!.amapPresentation1,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
@@ -60,6 +61,9 @@ class PresentationPage extends HookConsumerWidget {
),
recognizer: TapGestureRecognizer()
..onTap = () async {
+ final errorLinkMsg = AppLocalizations.of(
+ context,
+ )!.amapErrorLink;
try {
await launchUrl(
Uri.parse(info.link),
@@ -68,23 +72,22 @@ class PresentationPage extends HookConsumerWidget {
} catch (e) {
displayToastWithContext(
TypeMsg.msg,
- AMAPTextConstants.errorLink,
+ errorLinkMsg,
);
}
},
),
- error: (Object error, StackTrace stackTrace) =>
- const TextSpan(
- text: AMAPTextConstants.loadingError,
- style: TextStyle(color: Colors.red),
- ),
- loading: () => const TextSpan(
- text: AMAPTextConstants.loading,
- style: TextStyle(color: Colors.red),
+ error: (Object error, StackTrace stackTrace) => TextSpan(
+ text: AppLocalizations.of(context)!.amapLoadingError,
+ style: const TextStyle(color: Colors.red),
+ ),
+ loading: () => TextSpan(
+ text: AppLocalizations.of(context)!.amapLoading,
+ style: const TextStyle(color: Colors.red),
),
),
TextSpan(
- text: AMAPTextConstants.presentation2,
+ text: AppLocalizations.of(context)!.amapPresentation2,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
@@ -109,7 +112,7 @@ class PresentationPage extends HookConsumerWidget {
AsyncChild(
value: information,
builder: (context, info) => Text(
- "${AMAPTextConstants.contact} : ${info.manager} ",
+ "${AppLocalizations.of(context)!.amapContact} : ${info.manager} ",
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
diff --git a/lib/amap/ui/pages/product_pages/add_edit_product.dart b/lib/amap/ui/pages/product_pages/add_edit_product.dart
index efdec80220..5c716ce56c 100644
--- a/lib/amap/ui/pages/product_pages/add_edit_product.dart
+++ b/lib/amap/ui/pages/product_pages/add_edit_product.dart
@@ -16,6 +16,7 @@ import 'package:titan/tools/ui/widgets/align_left_text.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/tools/ui/widgets/text_entry.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditProduct extends HookConsumerWidget {
const AddEditProduct({super.key});
@@ -34,7 +35,7 @@ class AddEditProduct extends HookConsumerWidget {
);
final beginState = isEdit
? product.category
- : AMAPTextConstants.createCategory;
+ : AppLocalizations.of(context)!.amapCreateCategory;
final categoryController = ref.watch(selectedCategoryProvider(beginState));
final categoryNotifier = ref.watch(
selectedCategoryProvider(beginState).notifier,
@@ -57,8 +58,8 @@ class AddEditProduct extends HookConsumerWidget {
const SizedBox(height: 10),
AlignLeftText(
isEdit
- ? AMAPTextConstants.editProduct
- : AMAPTextConstants.addProduct,
+ ? AppLocalizations.of(context)!.amapEditProduct
+ : AppLocalizations.of(context)!.amapAddProduct,
color: AMAPColorConstants.green2,
),
const SizedBox(height: 40),
@@ -66,7 +67,7 @@ class AddEditProduct extends HookConsumerWidget {
children: [
Center(
child: TextEntry(
- label: AMAPTextConstants.name,
+ label: AppLocalizations.of(context)!.amapName,
controller: nameController,
color: AMAPColorConstants.greenGradient2,
enabledColor: AMAPColorConstants.enabled,
@@ -75,7 +76,7 @@ class AddEditProduct extends HookConsumerWidget {
const SizedBox(height: 30),
Center(
child: TextEntry(
- label: AMAPTextConstants.price,
+ label: AppLocalizations.of(context)!.amapPrice,
isDouble: true,
color: AMAPColorConstants.greenGradient2,
enabledColor: AMAPColorConstants.enabled,
@@ -84,15 +85,15 @@ class AddEditProduct extends HookConsumerWidget {
),
),
const SizedBox(height: 30),
- const AlignLeftText(
- AMAPTextConstants.category,
+ AlignLeftText(
+ AppLocalizations.of(context)!.amapCategory,
fontSize: 20,
color: AMAPColorConstants.greenGradient2,
),
const SizedBox(height: 10),
Center(
child: DropdownButtonFormField(
- value: categoryController,
+ initialValue: categoryController,
decoration: const InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
@@ -110,32 +111,41 @@ class AddEditProduct extends HookConsumerWidget {
),
),
),
- items: [AMAPTextConstants.createCategory, ...categories]
- .map((String value) {
+ items:
+ [
+ AppLocalizations.of(context)!.amapCreateCategory,
+ ...categories,
+ ].map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
- })
- .toList(),
+ }).toList(),
onChanged: (value) {
categoryNotifier.setText(
- value ?? AMAPTextConstants.createCategory,
+ value ??
+ AppLocalizations.of(
+ context,
+ )!.amapCreateCategory,
);
newCategory.text = "";
},
),
),
if (categoryController ==
- AMAPTextConstants.createCategory) ...[
+ AppLocalizations.of(context)!.amapCreateCategory) ...[
const SizedBox(height: 30),
Center(
child: TextEntry(
- label: AMAPTextConstants.createCategory,
- noValueError: AMAPTextConstants.pickChooseCategory,
+ label: AppLocalizations.of(
+ context,
+ )!.amapCreateCategory,
+ noValueError: AppLocalizations.of(
+ context,
+ )!.amapPickChooseCategory,
enabled:
categoryController ==
- AMAPTextConstants.createCategory,
+ AppLocalizations.of(context)!.amapCreateCategory,
onChanged: (value) {
newCategory.text = value;
newCategory.selection = TextSelection.fromPosition(
@@ -162,7 +172,9 @@ class AddEditProduct extends HookConsumerWidget {
if (formKey.currentState!.validate()) {
String cate =
categoryController ==
- AMAPTextConstants.createCategory
+ AppLocalizations.of(
+ context,
+ )!.amapCreateCategory
? newCategory.text
: categoryController;
Product newProduct = Product(
@@ -175,6 +187,18 @@ class AddEditProduct extends HookConsumerWidget {
quantity: 0,
);
await tokenExpireWrapper(ref, () async {
+ final updatedProductMsg = isEdit
+ ? AppLocalizations.of(
+ context,
+ )!.amapUpdatedProduct
+ : AppLocalizations.of(
+ context,
+ )!.amapAddedProduct;
+ final addingErrorMsg = isEdit
+ ? AppLocalizations.of(
+ context,
+ )!.amapUpdatingError
+ : AppLocalizations.of(context)!.amapAddingError;
final value = isEdit
? await productsNotifier.updateProduct(
newProduct,
@@ -183,10 +207,6 @@ class AddEditProduct extends HookConsumerWidget {
if (value) {
if (isEdit) {
formKey.currentState!.reset();
- displayToastWithContext(
- TypeMsg.msg,
- AMAPTextConstants.updatedProduct,
- );
} else {
ref
.watch(selectedListProvider.notifier)
@@ -196,23 +216,16 @@ class AddEditProduct extends HookConsumerWidget {
orElse: () => [],
),
);
- displayToastWithContext(
- TypeMsg.msg,
- AMAPTextConstants.addedProduct,
- );
}
+ displayToastWithContext(
+ TypeMsg.msg,
+ updatedProductMsg,
+ );
} else {
- if (isEdit) {
- displayToastWithContext(
- TypeMsg.error,
- AMAPTextConstants.updatingError,
- );
- } else {
- displayToastWithContext(
- TypeMsg.error,
- AMAPTextConstants.addingError,
- );
- }
+ displayToastWithContext(
+ TypeMsg.error,
+ addingErrorMsg,
+ );
}
QR.back();
});
@@ -220,8 +233,8 @@ class AddEditProduct extends HookConsumerWidget {
},
child: Text(
isEdit
- ? AMAPTextConstants.update
- : AMAPTextConstants.add,
+ ? AppLocalizations.of(context)!.amapUpdate
+ : AppLocalizations.of(context)!.amapAdd,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
diff --git a/lib/auth/providers/openid_provider.dart b/lib/auth/providers/openid_provider.dart
index 9fc42216ad..d0c3bcf3c8 100644
--- a/lib/auth/providers/openid_provider.dart
+++ b/lib/auth/providers/openid_provider.dart
@@ -19,10 +19,7 @@ final authTokenProvider =
StateNotifierProvider>>(
(ref) {
OpenIdTokenProvider openIdTokenProvider = OpenIdTokenProvider();
- final isConnected = ref.watch(isConnectedProvider);
- if (isConnected) {
- openIdTokenProvider.getTokenFromStorage();
- }
+ openIdTokenProvider.getTokenFromStorage();
return openIdTokenProvider;
},
);
@@ -123,8 +120,11 @@ class OpenIdTokenProvider
final String refreshTokenKey = "refresh_token";
final String redirectURLScheme = "${getTitanPackageName()}://authorized";
final String redirectURL = "${getTitanURL()}/static.html";
- final String discoveryUrl =
- "${Repository.host}.well-known/openid-configuration";
+ final AuthorizationServiceConfiguration authorizationServiceConfiguration =
+ AuthorizationServiceConfiguration(
+ authorizationEndpoint: "${Repository.host}auth/authorize",
+ tokenEndpoint: "${Repository.host}auth/token",
+ );
final List scopes = ["API"];
OpenIdTokenProvider() : super(const AsyncValue.loading());
@@ -221,7 +221,7 @@ class OpenIdTokenProvider
AuthorizationTokenRequest(
clientId,
redirectURLScheme,
- discoveryUrl: discoveryUrl,
+ serviceConfiguration: authorizationServiceConfiguration,
scopes: scopes,
allowInsecureConnections: kDebugMode,
),
@@ -239,7 +239,8 @@ class OpenIdTokenProvider
Future getTokenFromStorage() async {
state = const AsyncValue.loading();
- _secureStorage.read(key: tokenName).then((token) async {
+ try {
+ final token = await _secureStorage.read(key: tokenName);
if (token != null) {
try {
if (kIsWeb) {
@@ -262,7 +263,7 @@ class OpenIdTokenProvider
TokenRequest(
clientId,
redirectURLScheme,
- discoveryUrl: discoveryUrl,
+ serviceConfiguration: authorizationServiceConfiguration,
scopes: scopes,
refreshToken: token,
allowInsecureConnections: kDebugMode,
@@ -280,51 +281,62 @@ class OpenIdTokenProvider
} else {
state = const AsyncValue.error("No token found", StackTrace.empty);
}
- });
+ } catch (e) {
+ state = AsyncValue.error(e, StackTrace.empty);
+ }
}
Future getAuthToken(String authorizationToken) async {
- appAuth
- .token(
- TokenRequest(
- clientId,
- redirectURLScheme,
- discoveryUrl: discoveryUrl,
- scopes: scopes,
- authorizationCode: authorizationToken,
- allowInsecureConnections: kDebugMode,
- ),
- )
- .then((resp) {
- state = AsyncValue.data({
- tokenKey: resp.accessToken!,
- refreshTokenKey: resp.refreshToken!,
- });
- });
+ try {
+ final resp = await appAuth.token(
+ TokenRequest(
+ clientId,
+ redirectURLScheme,
+ serviceConfiguration: authorizationServiceConfiguration,
+ scopes: scopes,
+ authorizationCode: authorizationToken,
+ allowInsecureConnections: kDebugMode,
+ ),
+ );
+ state = AsyncValue.data({
+ tokenKey: resp.accessToken!,
+ refreshTokenKey: resp.refreshToken!,
+ });
+ } catch (e) {
+ state = AsyncValue.error(e, StackTrace.empty);
+ }
}
Future refreshToken() async {
return state.when(
data: (token) async {
if (token[refreshTokenKey] != null && token[refreshTokenKey] != "") {
- TokenResponse? resp = await appAuth.token(
- TokenRequest(
- clientId,
- redirectURLScheme,
- discoveryUrl: discoveryUrl,
- scopes: scopes,
- refreshToken: token[refreshTokenKey] as String,
- allowInsecureConnections: kDebugMode,
- ),
- );
- state = AsyncValue.data({
- tokenKey: resp.accessToken!,
- refreshTokenKey: resp.refreshToken!,
- });
- storeToken();
- return true;
+ try {
+ TokenResponse? resp = await appAuth.token(
+ TokenRequest(
+ clientId,
+ redirectURLScheme,
+ serviceConfiguration: authorizationServiceConfiguration,
+ scopes: scopes,
+ refreshToken: token[refreshTokenKey] as String,
+ allowInsecureConnections: kDebugMode,
+ ),
+ );
+ state = AsyncValue.data({
+ tokenKey: resp.accessToken!,
+ refreshTokenKey: resp.refreshToken!,
+ });
+ storeToken();
+ return true;
+ } catch (e) {
+ state = AsyncValue.error(e, StackTrace.empty);
+ return false;
+ }
}
- state = const AsyncValue.error(e, StackTrace.empty);
+ state = const AsyncValue.error(
+ "No refresh token available",
+ StackTrace.empty,
+ );
return false;
},
error: (error, stackTrace) {
diff --git a/lib/booking/router.dart b/lib/booking/router.dart
index 27abc4f2ef..7821bb328b 100644
--- a/lib/booking/router.dart
+++ b/lib/booking/router.dart
@@ -1,6 +1,5 @@
-import 'package:either_dart/either.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
import 'package:titan/booking/providers/is_admin_provider.dart';
import 'package:titan/booking/providers/is_manager_provider.dart';
import 'package:titan/booking/ui/pages/admin_pages/add_edit_manager_page.dart'
@@ -17,7 +16,8 @@ import 'package:titan/booking/ui/pages/manager_page/manager_page.dart'
deferred as manager_page;
import 'package:titan/booking/ui/pages/admin_pages/add_edit_room_page.dart'
deferred as add_edit_room_page;
-import 'package:titan/drawer/class/module.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
import 'package:titan/tools/middlewares/admin_middleware.dart';
import 'package:titan/tools/middlewares/authenticated_middleware.dart';
import 'package:titan/tools/middlewares/deferred_middleware.dart';
@@ -32,10 +32,10 @@ class BookingRouter {
static const String detail = '/detail';
static const String room = '/room';
static final Module module = Module(
- name: "Réservation",
- icon: const Left(HeroIcons.tableCells),
+ getName: (context) => AppLocalizations.of(context)!.moduleBooking,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleBookingDescription,
root: BookingRouter.root,
- selected: false,
);
BookingRouter(this.ref);
@@ -47,6 +47,10 @@ class BookingRouter {
AuthenticatedMiddleware(ref),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
children: [
QRoute(
path: admin,
diff --git a/lib/booking/tools/constants.dart b/lib/booking/tools/constants.dart
index c45327ae84..d34e7df9f4 100644
--- a/lib/booking/tools/constants.dart
+++ b/lib/booking/tools/constants.dart
@@ -1,113 +1,11 @@
import 'package:syncfusion_flutter_calendar/calendar.dart';
-class BookingTextConstants {
- static const String add = "Ajouter";
- static const String addBookingPage = "Demande";
- static const String addRoom = "Ajouter une salle";
- static const String addBooking = "Ajouter une réservation";
- static const String addedBooking = "Demande ajoutée";
- static const String addedRoom = "Salle ajoutée";
- static const String addedManager = "Gestionnaire ajouté";
- static const String addingError = "Erreur lors de l'ajout";
- static const String addManager = "Ajouter un gestionnaire";
- static const String adminPage = "Administrateur";
- static const String allDay = "Toute la journée";
- static const String bookedfor = "Réservé pour";
- static const String booking = "Réservation";
- static const String bookingCreated = "Réservation créée";
- static const String bookingDemand = "Demande de réservation";
- static const String bookingNote = "Note de la réservation";
- static const String bookingPage = "Réservation";
- static const String bookingReason = "Motif de la réservation";
- static const String by = "par";
- static const String confirm = "Confirmer";
- static const String confirmation = "Confirmation";
- static const String confirmBooking = "Confirmer la réservation ?";
- static const String confirmed = "Validée";
- static const String dates = "Dates";
- static const String decline = "Refuser";
- static const String declineBooking = "Refuser la réservation ?";
- static const String declined = "Refusée";
- static const String delete = "Supprimer";
- static const String deleting = "Suppression";
- static const String deleteBooking = "Suppression";
- static const String deleteBookingConfirmation =
- "Êtes-vous sûr de vouloir supprimer cette réservation ?";
- static const String deletedBooking = "Réservation supprimée";
- static const String deletedRoom = "Salle supprimée";
- static const String deletedManager = "Gestionnaire supprimé";
- static const String deleteRoomConfirmation =
- "Êtes-vous sûr de vouloir supprimer cette salle ?\n\nLa salle ne doit avoir aucune réservation en cours ou à venir pour être supprimée";
- static const String deleteManagerConfirmation =
- "Êtes-vous sûr de vouloir supprimer ce gestionnaire ?\n\nLe gestionnaire ne doit être associé à aucune salle pour pouvoir être supprimé";
- static const String deletingBooking = "Supprimer la réservation ?";
- static const String deletingError = "Erreur lors de la suppression";
- static const String deletingRoom = "Supprimer la salle ?";
- static const String edit = "Modifier";
- static const String editBooking = "Modifier une réservation";
- static const String editionError = "Erreur lors de la modification";
- static const String editedBooking = "Réservation modifiée";
- static const String editedRoom = "Salle modifiée";
- static const String editedManager = "Gestionnaire modifié";
- static const String editManager = "Modifier ou supprimer un gestionnaire";
- static const String editRoom = "Modifier ou supprimer une salle";
- static const String endDate = "Date de fin";
- static const String endHour = "Heure de fin";
- static const String entity = "Pour qui ?";
- static const String error = "Erreur";
- static const String eventEvery = "Tous les";
- static const String historyPage = "Historique";
- static const String incorrectOrMissingFields =
- "Champs incorrects ou manquants";
- static const String interval = "Intervalle";
- static const String invalidIntervalError = "Intervalle invalide";
- static const String invalidDates = "Dates invalides";
- static const String invalidRoom = "Salle invalide";
- static const String keysRequested = "Clés demandées";
- static const String management = "Gestion";
- static const String manager = "Gestionnaire";
- static const String managerName = "Nom du gestionnaire";
- static const String multipleDay = "Plusieurs jours";
- static const String myBookings = "Mes réservations";
- static const String necessaryKey = "Clé nécessaire";
- static const String next = "Suivant";
- static const String no = "Non";
- static const String noCurrentBooking = "Pas de réservation en cours";
- static const String noDateError = "Veuillez choisir une date";
- static const String noAppointmentInReccurence =
- "Aucun créneau existe avec ces paramètres de récurrence";
- static const String noDaySelected = "Aucun jour sélectionné";
- static const String noDescriptionError = "Veuillez entrer une description";
- static const String noKeys = "Aucune clé";
- static const String noNoteError = "Veuillez entrer une note";
- static const String noPhoneRegistered = "Numéro non renseigné";
- static const String noReasonError = "Veuillez entrer un motif";
- static const String noRoomFoundError = "Aucune salle enregistrée";
- static const String noRoomFound = "Aucune salle trouvée";
- static const String note = "Note";
- static const String other = "Autre";
- static const String pending = "En attente";
- static const String previous = "Précédent";
- static const String reason = "Motif";
- static const String recurrence = "Récurrence";
- static const String recurrenceDays = "Jours de récurrence";
- static const String recurrenceEndDate = "Date de fin de récurrence";
- static const String recurrent = "Récurrent";
- static const String registeredRooms = "Salles enregistrées";
- static const String room = "Salle";
- static const String roomName = "Nom de la salle";
- static const String startDate = "Date de début";
- static const String startHour = "Heure de début";
- static const String weeks = "Semaines";
- static const String yes = "Oui";
-
- static const List weekDaysOrdered = [
- WeekDays.monday,
- WeekDays.tuesday,
- WeekDays.wednesday,
- WeekDays.thursday,
- WeekDays.friday,
- WeekDays.saturday,
- WeekDays.sunday,
- ];
-}
+final weekDaysOrdered = [
+ WeekDays.monday,
+ WeekDays.tuesday,
+ WeekDays.wednesday,
+ WeekDays.thursday,
+ WeekDays.friday,
+ WeekDays.saturday,
+ WeekDays.sunday,
+];
diff --git a/lib/booking/tools/functions.dart b/lib/booking/tools/functions.dart
index e65867d478..92caafbf6b 100644
--- a/lib/booking/tools/functions.dart
+++ b/lib/booking/tools/functions.dart
@@ -1,33 +1,35 @@
-import 'package:titan/booking/tools/constants.dart';
+import 'package:flutter/material.dart';
import 'package:titan/tools/functions.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
-String decisionToString(Decision d) {
+String decisionToString(Decision d, BuildContext context) {
switch (d) {
case Decision.approved:
- return BookingTextConstants.confirmed;
+ return AppLocalizations.of(context)!.bookingConfirmed;
case Decision.declined:
- return BookingTextConstants.declined;
+ return AppLocalizations.of(context)!.bookingDeclined;
case Decision.pending:
- return BookingTextConstants.pending;
+ return AppLocalizations.of(context)!.bookingPending;
}
}
-String weekDayToString(WeekDays day) {
+String weekDayToLocalizedString(BuildContext context, WeekDays day) {
+ final loc = AppLocalizations.of(context)!;
switch (day) {
- case WeekDays.sunday:
- return "Dimanche";
case WeekDays.monday:
- return "Lundi";
+ return loc.bookingWeekDayMon;
case WeekDays.tuesday:
- return "Mardi";
+ return loc.bookingWeekDayTue;
case WeekDays.wednesday:
- return "Mercredi";
+ return loc.bookingWeekDayWed;
case WeekDays.thursday:
- return "Jeudi";
+ return loc.bookingWeekDayThu;
case WeekDays.friday:
- return "Vendredi";
+ return loc.bookingWeekDayFri;
case WeekDays.saturday:
- return "Samedi";
+ return loc.bookingWeekDaySat;
+ case WeekDays.sunday:
+ return loc.bookingWeekDaySun;
}
}
diff --git a/lib/booking/ui/booking.dart b/lib/booking/ui/booking.dart
index 1371781d90..fe9a136d95 100644
--- a/lib/booking/ui/booking.dart
+++ b/lib/booking/ui/booking.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:titan/booking/router.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/tools/ui/widgets/top_bar.dart';
+import 'package:titan/tools/constants.dart';
class BookingTemplate extends StatelessWidget {
final Widget child;
@@ -9,16 +9,18 @@ class BookingTemplate extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return SafeArea(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- const TopBar(
- title: BookingTextConstants.booking,
- root: BookingRouter.root,
+ return Scaffold(
+ body: Container(
+ decoration: const BoxDecoration(color: ColorConstants.background),
+ child: SafeArea(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ const TopBar(root: BookingRouter.root),
+ Expanded(child: child),
+ ],
),
- Expanded(child: child),
- ],
+ ),
),
);
}
diff --git a/lib/booking/ui/calendar/calendar.dart b/lib/booking/ui/calendar/calendar.dart
index 61d1559a65..ee15f535c5 100644
--- a/lib/booking/ui/calendar/calendar.dart
+++ b/lib/booking/ui/calendar/calendar.dart
@@ -6,7 +6,7 @@ import 'package:titan/booking/providers/confirmed_booking_list_provider.dart';
import 'package:titan/booking/providers/manager_confirmed_booking_list_provider.dart';
import 'package:titan/booking/ui/calendar/appointment_data_source.dart';
import 'package:titan/booking/ui/calendar/calendar_dialog.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
+import 'package:titan/navigation/providers/is_web_format_provider.dart';
import 'package:titan/tools/constants.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
diff --git a/lib/booking/ui/calendar/calendar_dialog.dart b/lib/booking/ui/calendar/calendar_dialog.dart
index 9a8b0027f3..819e8576f1 100644
--- a/lib/booking/ui/calendar/calendar_dialog.dart
+++ b/lib/booking/ui/calendar/calendar_dialog.dart
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:titan/booking/class/booking.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/calendar/calendar_dialog_button.dart';
import 'package:titan/tools/functions.dart';
+import 'package:titan/l10n/app_localizations.dart';
class CalendarDialog extends StatelessWidget {
final Booking booking;
@@ -17,6 +17,7 @@ class CalendarDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final locale = Localizations.localeOf(context);
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
child: Stack(
@@ -41,6 +42,7 @@ class CalendarDialog extends StatelessWidget {
booking.end,
booking.recurrenceRule,
false,
+ locale.toString(),
),
style: TextStyle(
fontWeight: FontWeight.w400,
@@ -50,7 +52,7 @@ class CalendarDialog extends StatelessWidget {
),
const SizedBox(height: 10),
Text(
- "${BookingTextConstants.bookedfor} ${booking.entity} ${BookingTextConstants.by} ${booking.applicant.getName()}",
+ "${AppLocalizations.of(context)!.bookingBookedFor} ${booking.entity} ${AppLocalizations.of(context)!.bookingBy} ${booking.applicant.getName()}",
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 15,
@@ -103,7 +105,9 @@ class CalendarDialog extends StatelessWidget {
Flexible(
child: Text(
booking.applicant.phone ??
- BookingTextConstants.noPhoneRegistered,
+ AppLocalizations.of(
+ context,
+ )!.bookingNoPhoneRegistered,
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 15,
diff --git a/lib/booking/ui/components/booking_card.dart b/lib/booking/ui/components/booking_card.dart
index fb3df46a66..3c6d094d74 100644
--- a/lib/booking/ui/components/booking_card.dart
+++ b/lib/booking/ui/components/booking_card.dart
@@ -2,12 +2,12 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:titan/booking/class/booking.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/tools/ui/layouts/card_button.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
class BookingCard extends StatelessWidget {
final Booking booking;
@@ -29,6 +29,7 @@ class BookingCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final locale = Localizations.localeOf(context);
final isNotEnded = booking.recurrenceRule.isNotEmpty
? SfCalendar.parseRRule(
booking.recurrenceRule,
@@ -126,6 +127,7 @@ class BookingCard extends StatelessWidget {
booking.end,
booking.recurrenceRule,
false,
+ locale.toString(),
),
style: TextStyle(
fontSize: 13,
@@ -153,10 +155,10 @@ class BookingCard extends StatelessWidget {
children: [
Text(
booking.decision == Decision.pending
- ? BookingTextConstants.pending
+ ? AppLocalizations.of(context)!.bookingPending
: booking.decision == Decision.approved
- ? BookingTextConstants.confirmed
- : BookingTextConstants.declined,
+ ? AppLocalizations.of(context)!.bookingConfirmed
+ : AppLocalizations.of(context)!.bookingDeclined,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
@@ -164,7 +166,7 @@ class BookingCard extends StatelessWidget {
),
),
Text(
- '${BookingTextConstants.keysRequested}: ${booking.key ? BookingTextConstants.yes : BookingTextConstants.no}',
+ '${AppLocalizations.of(context)!.bookingKeysRequested}: ${booking.key ? AppLocalizations.of(context)!.bookingYes : AppLocalizations.of(context)!.bookingNo}',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
diff --git a/lib/booking/ui/pages/admin_pages/add_edit_manager_page.dart b/lib/booking/ui/pages/admin_pages/add_edit_manager_page.dart
index e89d0c1405..c02ac990b2 100644
--- a/lib/booking/ui/pages/admin_pages/add_edit_manager_page.dart
+++ b/lib/booking/ui/pages/admin_pages/add_edit_manager_page.dart
@@ -6,7 +6,6 @@ import 'package:titan/admin/providers/group_id_provider.dart';
import 'package:titan/booking/class/manager.dart';
import 'package:titan/booking/providers/manager_list_provider.dart';
import 'package:titan/booking/providers/manager_provider.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/booking.dart';
import 'package:titan/booking/ui/pages/admin_pages/admin_entry.dart';
import 'package:titan/booking/ui/pages/admin_pages/admin_scroll_chips.dart';
@@ -17,6 +16,7 @@ import 'package:titan/tools/ui/layouts/item_chip.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:qlevar_router/qlevar_router.dart';
import 'package:titan/admin/providers/group_list_provider.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditManagerPage extends HookConsumerWidget {
final GlobalKey dataKey = GlobalKey();
@@ -46,8 +46,8 @@ class AddEditManagerPage extends HookConsumerWidget {
alignment: Alignment.centerLeft,
child: Text(
isEdit
- ? BookingTextConstants.editManager
- : BookingTextConstants.addManager,
+ ? AppLocalizations.of(context)!.bookingEditManager
+ : AppLocalizations.of(context)!.bookingAddManager,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -61,7 +61,7 @@ class AddEditManagerPage extends HookConsumerWidget {
children: [
const SizedBox(height: 50),
AdminEntry(
- name: BookingTextConstants.managerName,
+ name: AppLocalizations.of(context)!.bookingManagerName,
nameController: name,
),
const SizedBox(height: 50),
@@ -105,6 +105,12 @@ class AddEditManagerPage extends HookConsumerWidget {
name: name.text,
groupId: groupId,
);
+ final editedManagerMsg = isEdit
+ ? AppLocalizations.of(context)!.bookingEditedManager
+ : AppLocalizations.of(context)!.bookingAddedManager;
+ final editedManagerErrorMsg = isEdit
+ ? AppLocalizations.of(context)!.bookingEditionError
+ : AppLocalizations.of(context)!.bookingAddingError;
final value = isEdit
? await managerListNotifier.updateManager(
newManager,
@@ -112,31 +118,21 @@ class AddEditManagerPage extends HookConsumerWidget {
: await managerListNotifier.addManager(newManager);
if (value) {
QR.back();
- isEdit
- ? displayToastWithContext(
- TypeMsg.msg,
- BookingTextConstants.editedManager,
- )
- : displayToastWithContext(
- TypeMsg.msg,
- BookingTextConstants.addedManager,
- );
+ displayToastWithContext(
+ TypeMsg.msg,
+ editedManagerMsg,
+ );
} else {
- isEdit
- ? displayToastWithContext(
- TypeMsg.error,
- BookingTextConstants.editionError,
- )
- : displayToastWithContext(
- TypeMsg.error,
- BookingTextConstants.addingError,
- );
+ displayToastWithContext(
+ TypeMsg.error,
+ editedManagerErrorMsg,
+ );
}
});
},
buttonText: isEdit
- ? BookingTextConstants.edit
- : BookingTextConstants.add,
+ ? AppLocalizations.of(context)!.bookingEdit
+ : AppLocalizations.of(context)!.bookingAdd,
),
if (isEdit) ...[
const SizedBox(height: 30),
@@ -146,30 +142,39 @@ class AddEditManagerPage extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => CustomDialogBox(
- descriptions: BookingTextConstants
- .deleteManagerConfirmation,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.bookingDeleteManagerConfirmation,
onYes: () async {
+ final deletedManagerMsg = AppLocalizations.of(
+ context,
+ )!.bookingDeletedManager;
+ final deletingErrorMsg = AppLocalizations.of(
+ context,
+ )!.bookingDeletingError;
final value = await managerListNotifier
.deleteManager(manager);
if (value) {
QR.back();
displayToastWithContext(
TypeMsg.msg,
- BookingTextConstants.deletedManager,
+ deletedManagerMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- BookingTextConstants.deletingError,
+ deletingErrorMsg,
);
}
},
- title: BookingTextConstants.deleting,
+ title: AppLocalizations.of(
+ context,
+ )!.bookingDeleting,
),
);
});
},
- buttonText: BookingTextConstants.delete,
+ buttonText: AppLocalizations.of(context)!.bookingDelete,
),
],
const SizedBox(height: 30),
diff --git a/lib/booking/ui/pages/admin_pages/add_edit_room_page.dart b/lib/booking/ui/pages/admin_pages/add_edit_room_page.dart
index 009859b011..17a2da96f0 100644
--- a/lib/booking/ui/pages/admin_pages/add_edit_room_page.dart
+++ b/lib/booking/ui/pages/admin_pages/add_edit_room_page.dart
@@ -7,7 +7,6 @@ import 'package:titan/booking/providers/manager_list_provider.dart';
import 'package:titan/booking/providers/manager_id_provider.dart';
import 'package:titan/service/providers/room_list_provider.dart';
import 'package:titan/booking/providers/room_provider.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/booking.dart';
import 'package:titan/booking/ui/pages/admin_pages/admin_entry.dart';
import 'package:titan/booking/ui/pages/admin_pages/admin_scroll_chips.dart';
@@ -17,6 +16,7 @@ import 'package:titan/tools/token_expire_wrapper.dart';
import 'package:titan/tools/ui/layouts/item_chip.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditRoomPage extends HookConsumerWidget {
final dataKey = GlobalKey();
@@ -46,8 +46,8 @@ class AddEditRoomPage extends HookConsumerWidget {
alignment: Alignment.centerLeft,
child: Text(
isEdit
- ? BookingTextConstants.editRoom
- : BookingTextConstants.addRoom,
+ ? AppLocalizations.of(context)!.bookingEditRoom
+ : AppLocalizations.of(context)!.bookingAddRoom,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -61,7 +61,7 @@ class AddEditRoomPage extends HookConsumerWidget {
children: [
const SizedBox(height: 50),
AdminEntry(
- name: BookingTextConstants.roomName,
+ name: AppLocalizations.of(context)!.bookingRoomName,
nameController: name,
),
const SizedBox(height: 50),
@@ -105,36 +105,29 @@ class AddEditRoomPage extends HookConsumerWidget {
name: name.text,
managerId: managerId,
);
+ final editedRoomMsg = isEdit
+ ? AppLocalizations.of(context)!.bookingEditedRoom
+ : AppLocalizations.of(context)!.bookingAddedRoom;
+ final addingErrorMsg = isEdit
+ ? AppLocalizations.of(context)!.bookingEditionError
+ : AppLocalizations.of(context)!.bookingAddingError;
final value = isEdit
? await roomListNotifier.updateRoom(newRoom)
: await roomListNotifier.addRoom(newRoom);
if (value) {
QR.back();
- isEdit
- ? displayToastWithContext(
- TypeMsg.msg,
- BookingTextConstants.editedRoom,
- )
- : displayToastWithContext(
- TypeMsg.msg,
- BookingTextConstants.addedRoom,
- );
+ displayToastWithContext(TypeMsg.msg, editedRoomMsg);
} else {
- isEdit
- ? displayToastWithContext(
- TypeMsg.error,
- BookingTextConstants.editionError,
- )
- : displayToastWithContext(
- TypeMsg.error,
- BookingTextConstants.addingError,
- );
+ displayToastWithContext(
+ TypeMsg.error,
+ addingErrorMsg,
+ );
}
});
},
buttonText: isEdit
- ? BookingTextConstants.edit
- : BookingTextConstants.add,
+ ? AppLocalizations.of(context)!.bookingEdit
+ : AppLocalizations.of(context)!.bookingAdd,
),
if (isEdit) ...[
const SizedBox(height: 30),
@@ -144,9 +137,16 @@ class AddEditRoomPage extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => CustomDialogBox(
- descriptions:
- BookingTextConstants.deleteRoomConfirmation,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.bookingDeleteRoomConfirmation,
onYes: () async {
+ final deletedRoomMsg = AppLocalizations.of(
+ context,
+ )!.bookingDeletedRoom;
+ final deletingErrorMsg = AppLocalizations.of(
+ context,
+ )!.bookingDeletingError;
final value = await roomListNotifier.deleteRoom(
room,
);
@@ -154,21 +154,23 @@ class AddEditRoomPage extends HookConsumerWidget {
QR.back();
displayToastWithContext(
TypeMsg.msg,
- BookingTextConstants.deletedRoom,
+ deletedRoomMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- BookingTextConstants.deletingError,
+ deletingErrorMsg,
);
}
},
- title: BookingTextConstants.deleteBooking,
+ title: AppLocalizations.of(
+ context,
+ )!.bookingDeleteBooking,
),
);
});
},
- buttonText: BookingTextConstants.delete,
+ buttonText: AppLocalizations.of(context)!.bookingDelete,
),
],
const SizedBox(height: 30),
diff --git a/lib/booking/ui/pages/admin_pages/admin_page.dart b/lib/booking/ui/pages/admin_pages/admin_page.dart
index ef481dac5e..3cba6f3e47 100644
--- a/lib/booking/ui/pages/admin_pages/admin_page.dart
+++ b/lib/booking/ui/pages/admin_pages/admin_page.dart
@@ -13,13 +13,13 @@ import 'package:titan/booking/providers/manager_provider.dart';
import 'package:titan/service/providers/room_list_provider.dart';
import 'package:titan/booking/providers/room_provider.dart';
import 'package:titan/booking/router.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/booking.dart';
import 'package:titan/booking/ui/calendar/calendar.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/layouts/item_chip.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AdminPage extends HookConsumerWidget {
const AdminPage({super.key});
@@ -37,6 +37,7 @@ class AdminPage extends HookConsumerWidget {
return BookingTemplate(
child: LayoutBuilder(
builder: (context, constraints) => Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await ref.watch(roomListProvider.notifier).loadRooms();
await ref
@@ -53,13 +54,13 @@ class AdminPage extends HookConsumerWidget {
const SizedBox(height: 20),
const Expanded(child: Calendar(isManagerPage: false)),
const SizedBox(height: 30),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 30.0),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
- BookingTextConstants.room,
- style: TextStyle(
+ AppLocalizations.of(context)!.bookingRoom,
+ style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 149, 149, 149),
@@ -123,13 +124,13 @@ class AdminPage extends HookConsumerWidget {
},
),
const SizedBox(height: 20),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 30.0),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
- BookingTextConstants.manager,
- style: TextStyle(
+ AppLocalizations.of(context)!.bookingManager,
+ style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 149, 149, 149),
diff --git a/lib/booking/ui/pages/booking_pages/add_edit_booking_page.dart b/lib/booking/ui/pages/booking_pages/add_edit_booking_page.dart
index ff76e882a5..b287e3b79f 100644
--- a/lib/booking/ui/pages/booking_pages/add_edit_booking_page.dart
+++ b/lib/booking/ui/pages/booking_pages/add_edit_booking_page.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/booking/class/booking.dart';
import 'package:titan/service/class/room.dart';
import 'package:titan/booking/providers/booking_provider.dart';
@@ -29,6 +30,7 @@ import 'package:titan/tools/ui/widgets/text_entry.dart';
import 'package:titan/user/providers/user_provider.dart';
import 'package:qlevar_router/qlevar_router.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditBookingPage extends HookConsumerWidget {
final dataKey = GlobalKey();
@@ -38,6 +40,7 @@ class AddEditBookingPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final now = DateTime.now();
final user = ref.watch(userProvider);
final key = GlobalKey();
@@ -60,14 +63,14 @@ class AddEditBookingPage extends HookConsumerWidget {
text: isEdit
? recurrent.value
? processDateOnlyHour(booking.start)
- : processDateWithHour(booking.start)
+ : DateFormat.yMd(locale).add_Hm().format(booking.start)
: "",
);
final end = useTextEditingController(
text: isEdit
? recurrent.value
? processDateOnlyHour(booking.end)
- : processDateWithHour(booking.end)
+ : DateFormat.yMd(locale).add_Hm().format(booking.end)
: "",
);
final motif = useTextEditingController(text: booking.reason);
@@ -83,7 +86,7 @@ class AddEditBookingPage extends HookConsumerWidget {
);
final recurrenceEndDate = useTextEditingController(
text: booking.recurrenceRule != ""
- ? processDate(
+ ? DateFormat.yMd(locale).format(
DateTime.parse(
booking.recurrenceRule.split(";UNTIL=")[1].split(";")[0],
),
@@ -106,8 +109,8 @@ class AddEditBookingPage extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 30),
child: AlignLeftText(
isEdit
- ? BookingTextConstants.editBooking
- : BookingTextConstants.addBooking,
+ ? AppLocalizations.of(context)!.bookingEditBooking
+ : AppLocalizations.of(context)!.bookingAddBooking,
color: Colors.grey,
),
),
@@ -153,27 +156,27 @@ class AddEditBookingPage extends HookConsumerWidget {
children: [
TextEntry(
controller: entity,
- label: BookingTextConstants.entity,
+ label: AppLocalizations.of(context)!.bookingEntity,
),
const SizedBox(height: 30),
TextEntry(
controller: motif,
- label: BookingTextConstants.reason,
+ label: AppLocalizations.of(context)!.bookingReason,
),
const SizedBox(height: 30),
TextEntry(
- label: BookingTextConstants.note,
+ label: AppLocalizations.of(context)!.bookingNote,
controller: note,
canBeEmpty: true,
),
const SizedBox(height: 20),
CheckBoxEntry(
- title: BookingTextConstants.necessaryKey,
+ title: AppLocalizations.of(context)!.bookingNecessaryKey,
valueNotifier: keyRequired,
),
const SizedBox(height: 20),
CheckBoxEntry(
- title: BookingTextConstants.recurrence,
+ title: AppLocalizations.of(context)!.bookingRecurrence,
valueNotifier: recurrent,
onChanged: () {
start.text = "";
@@ -183,7 +186,7 @@ class AddEditBookingPage extends HookConsumerWidget {
),
const SizedBox(height: 20),
CheckBoxEntry(
- title: BookingTextConstants.allDay,
+ title: AppLocalizations.of(context)!.bookingAllDay,
valueNotifier: allDay,
onChanged: () {
start.text = "";
@@ -195,13 +198,15 @@ class AddEditBookingPage extends HookConsumerWidget {
recurrent.value
? Column(
children: [
- const Text(
- BookingTextConstants.recurrenceDays,
- style: TextStyle(color: Colors.black),
+ Text(
+ AppLocalizations.of(
+ context,
+ )!.bookingRecurrenceDays,
+ style: const TextStyle(color: Colors.black),
),
const SizedBox(height: 10),
Column(
- children: BookingTextConstants.weekDaysOrdered
+ children: weekDaysOrdered
.map(
(e) => GestureDetector(
onTap: () {
@@ -213,7 +218,10 @@ class AddEditBookingPage extends HookConsumerWidget {
MainAxisAlignment.spaceBetween,
children: [
Text(
- weekDayToString(e),
+ weekDayToLocalizedString(
+ context,
+ e,
+ ),
style: TextStyle(
color: Colors.grey.shade700,
fontSize: 16,
@@ -234,15 +242,21 @@ class AddEditBookingPage extends HookConsumerWidget {
.toList(),
),
const SizedBox(height: 20),
- const Text(
- BookingTextConstants.interval,
+ Text(
+ AppLocalizations.of(context)!.bookingInterval,
style: TextStyle(color: Colors.black),
),
const SizedBox(height: 10),
TextEntry(
- label: BookingTextConstants.interval,
- prefix: BookingTextConstants.eventEvery,
- suffix: BookingTextConstants.weeks,
+ label: AppLocalizations.of(
+ context,
+ )!.bookingInterval,
+ prefix: AppLocalizations.of(
+ context,
+ )!.bookingEventEvery,
+ suffix: AppLocalizations.of(
+ context,
+ )!.bookingWeeks,
controller: interval,
isInt: true,
),
@@ -254,14 +268,18 @@ class AddEditBookingPage extends HookConsumerWidget {
onTap: () =>
getOnlyHourDate(context, start),
controller: start,
- label: BookingTextConstants.startHour,
+ label: AppLocalizations.of(
+ context,
+ )!.bookingStartHour,
),
const SizedBox(height: 30),
DateEntry(
onTap: () =>
getOnlyHourDate(context, end),
controller: end,
- label: BookingTextConstants.endHour,
+ label: AppLocalizations.of(
+ context,
+ )!.bookingEndHour,
),
const SizedBox(height: 30),
],
@@ -270,7 +288,9 @@ class AddEditBookingPage extends HookConsumerWidget {
onTap: () =>
getOnlyDayDate(context, recurrenceEndDate),
controller: recurrenceEndDate,
- label: BookingTextConstants.recurrenceEndDate,
+ label: AppLocalizations.of(
+ context,
+ )!.bookingRecurrenceEndDate,
),
],
)
@@ -281,7 +301,9 @@ class AddEditBookingPage extends HookConsumerWidget {
? getOnlyDayDate(context, start)
: getFullDate(context, start),
controller: start,
- label: BookingTextConstants.startDate,
+ label: AppLocalizations.of(
+ context,
+ )!.bookingStartDate,
),
const SizedBox(height: 30),
DateEntry(
@@ -289,7 +311,9 @@ class AddEditBookingPage extends HookConsumerWidget {
? getOnlyDayDate(context, end)
: getFullDate(context, end),
controller: end,
- label: BookingTextConstants.endDate,
+ label: AppLocalizations.of(
+ context,
+ )!.bookingEndDate,
),
],
),
@@ -300,6 +324,18 @@ class AddEditBookingPage extends HookConsumerWidget {
if (key.currentState == null) {
return;
}
+ final editedBookingMsg = AppLocalizations.of(
+ context,
+ )!.bookingEditedBooking;
+ final addedBookingMsg = AppLocalizations.of(
+ context,
+ )!.bookingAddedBooking;
+ final editionErrorMsg = AppLocalizations.of(
+ context,
+ )!.bookingEditionError;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.bookingAddingError;
if (key.currentState!.validate()) {
if (allDay.value) {
start.text = "${start.text} 00:00";
@@ -307,35 +343,39 @@ class AddEditBookingPage extends HookConsumerWidget {
}
if (end.text.contains("/") &&
isDateBefore(
- processDateBack(end.text),
- processDateBack(start.text),
+ processDateBack(end.text, locale.toString()),
+ processDateBack(start.text, locale.toString()),
)) {
displayToast(
context,
TypeMsg.error,
- BookingTextConstants.invalidDates,
+ AppLocalizations.of(context)!.bookingInvalidDates,
);
} else if (room.value.id.isEmpty) {
displayToast(
context,
TypeMsg.error,
- BookingTextConstants.invalidRoom,
+ AppLocalizations.of(context)!.bookingInvalidRoom,
);
} else if (recurrent.value && selectedDays.isEmpty) {
displayToast(
context,
TypeMsg.error,
- BookingTextConstants.noDaySelected,
+ AppLocalizations.of(
+ context,
+ )!.bookingNoDaySelected,
);
} else {
String recurrenceRule = "";
String startString = start.text;
if (!startString.contains("/")) {
- startString = "${processDate(now)} $startString";
+ startString =
+ "${DateFormat.yMd(locale).format(now)} $startString";
}
String endString = end.text;
if (!endString.contains("/")) {
- endString = "${processDate(now)} $endString";
+ endString =
+ "${DateFormat.yMd(locale).format(now)} $endString";
}
if (recurrent.value) {
RecurrenceProperties recurrence =
@@ -344,17 +384,26 @@ class AddEditBookingPage extends HookConsumerWidget {
recurrence.recurrenceRange =
RecurrenceRange.endDate;
recurrence.endDate = DateTime.parse(
- processDateBack(recurrenceEndDate.text),
+ processDateBack(
+ recurrenceEndDate.text,
+ locale.toString(),
+ ),
);
recurrence.weekDays = selectedDays;
recurrence.interval = int.parse(interval.text);
recurrenceRule = SfCalendar.generateRRule(
recurrence,
DateTime.parse(
- processDateBackWithHour(startString),
+ processDateBackWithHour(
+ startString,
+ locale.toString(),
+ ),
),
DateTime.parse(
- processDateBackWithHour(endString),
+ processDateBackWithHour(
+ endString,
+ locale.toString(),
+ ),
),
);
try {
@@ -366,8 +415,9 @@ class AddEditBookingPage extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- BookingTextConstants
- .noAppointmentInReccurence,
+ AppLocalizations.of(
+ context,
+ )!.bookingNoAppointmentInReccurence,
);
return;
}
@@ -377,10 +427,16 @@ class AddEditBookingPage extends HookConsumerWidget {
id: isEdit ? booking.id : "",
reason: motif.text,
start: DateTime.parse(
- processDateBackWithHour(startString),
+ processDateBackWithHour(
+ startString,
+ locale.toString(),
+ ),
),
end: DateTime.parse(
- processDateBackWithHour(endString),
+ processDateBackWithHour(
+ endString,
+ locale.toString(),
+ ),
),
creation: DateTime.now(),
note: note.text.isEmpty ? null : note.text,
@@ -429,24 +485,24 @@ class AddEditBookingPage extends HookConsumerWidget {
if (isEdit) {
displayToastWithContext(
TypeMsg.msg,
- BookingTextConstants.editedBooking,
+ editedBookingMsg,
);
} else {
displayToastWithContext(
TypeMsg.msg,
- BookingTextConstants.addedBooking,
+ addedBookingMsg,
);
}
} else {
if (isEdit) {
displayToastWithContext(
TypeMsg.error,
- BookingTextConstants.editionError,
+ editionErrorMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- BookingTextConstants.addingError,
+ addingErrorMsg,
);
}
}
@@ -456,14 +512,16 @@ class AddEditBookingPage extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- BookingTextConstants.incorrectOrMissingFields,
+ AppLocalizations.of(
+ context,
+ )!.bookingIncorrectOrMissingFields,
);
}
},
child: Text(
isEdit
- ? BookingTextConstants.edit
- : BookingTextConstants.add,
+ ? AppLocalizations.of(context)!.bookingEdit
+ : AppLocalizations.of(context)!.bookingAdd,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
diff --git a/lib/booking/ui/pages/detail_pages/detail_booking.dart b/lib/booking/ui/pages/detail_pages/detail_booking.dart
index b8715de07a..094dcf7fcb 100644
--- a/lib/booking/ui/pages/detail_pages/detail_booking.dart
+++ b/lib/booking/ui/pages/detail_pages/detail_booking.dart
@@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/booking/providers/booking_provider.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/tools/functions.dart';
import 'package:titan/booking/ui/booking.dart';
import 'package:titan/booking/ui/components/booking_card.dart';
import 'package:titan/booking/ui/pages/detail_pages/contact_button.dart';
import 'package:titan/tools/functions.dart';
import 'package:url_launcher/url_launcher.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DetailBookingPage extends HookConsumerWidget {
final bool isAdmin;
@@ -56,7 +56,7 @@ class DetailBookingPage extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- decisionToString(booking.decision),
+ decisionToString(booking.decision, context),
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
@@ -115,7 +115,7 @@ class DetailBookingPage extends HookConsumerWidget {
Column(
children: [
AutoSizeText(
- "${BookingTextConstants.bookedfor} ${booking.entity}",
+ "${AppLocalizations.of(context)!.bookingBookedFor} ${booking.entity}",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -126,7 +126,9 @@ class DetailBookingPage extends HookConsumerWidget {
const SizedBox(height: 50),
Text(
booking.applicant.phone ??
- BookingTextConstants.noPhoneRegistered,
+ AppLocalizations.of(
+ context,
+ )!.bookingNoPhoneRegistered,
style: const TextStyle(fontSize: 25),
),
const SizedBox(height: 50),
diff --git a/lib/booking/ui/pages/main_page/main_page.dart b/lib/booking/ui/pages/main_page/main_page.dart
index 3282ce988d..98ae22f341 100644
--- a/lib/booking/ui/pages/main_page/main_page.dart
+++ b/lib/booking/ui/pages/main_page/main_page.dart
@@ -12,7 +12,6 @@ import 'package:titan/booking/providers/manager_booking_list_provider.dart';
import 'package:titan/booking/providers/selected_days_provider.dart';
import 'package:titan/booking/providers/user_booking_list_provider.dart';
import 'package:titan/booking/router.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/booking.dart';
import 'package:titan/booking/ui/calendar/calendar.dart';
import 'package:titan/booking/ui/components/booking_card.dart';
@@ -26,6 +25,7 @@ import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:qlevar_router/qlevar_router.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
class BookingMainPage extends HookConsumerWidget {
const BookingMainPage({super.key});
@@ -61,6 +61,7 @@ class BookingMainPage extends HookConsumerWidget {
return BookingTemplate(
child: LayoutBuilder(
builder: (context, constraints) => Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await confirmedBookingsNotifier.loadConfirmedBooking();
await bookingsNotifier.loadUserBookings();
@@ -80,7 +81,7 @@ class BookingMainPage extends HookConsumerWidget {
children: [
if (isManager)
AdminButton(
- text: BookingTextConstants.management,
+ text: AppLocalizations.of(context)!.bookingManagement,
onTap: () {
QR.to(BookingRouter.root + BookingRouter.manager);
},
@@ -97,13 +98,13 @@ class BookingMainPage extends HookConsumerWidget {
const SizedBox(height: 10),
const Expanded(child: Calendar(isManagerPage: false)),
const SizedBox(height: 30),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 30.0),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
- BookingTextConstants.myBookings,
- style: TextStyle(
+ AppLocalizations.of(context)!.bookingMyBookings,
+ style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 149, 149, 149),
@@ -151,11 +152,19 @@ class BookingMainPage extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => CustomDialogBox(
- descriptions: BookingTextConstants
- .deleteBookingConfirmation,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.bookingDeleteBookingConfirmation,
onYes: () async {
+ final deleteMsg = AppLocalizations.of(
+ context,
+ )!.bookingDeleteBooking;
+ final errorMsg = AppLocalizations.of(
+ context,
+ )!.bookingDeletingError;
final value = await bookingsNotifier
.deleteBooking(e);
+
if (value) {
ref
.read(
@@ -164,16 +173,18 @@ class BookingMainPage extends HookConsumerWidget {
.loadUserManageBookings;
displayToastWithContext(
TypeMsg.msg,
- BookingTextConstants.deleteBooking,
+ deleteMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- BookingTextConstants.deletingError,
+ errorMsg,
);
}
},
- title: BookingTextConstants.deleteBooking,
+ title: AppLocalizations.of(
+ context,
+ )!.bookingDeleteBooking,
),
);
});
diff --git a/lib/booking/ui/pages/manager_page/list_booking.dart b/lib/booking/ui/pages/manager_page/list_booking.dart
index 559337eef2..c5a928de3f 100644
--- a/lib/booking/ui/pages/manager_page/list_booking.dart
+++ b/lib/booking/ui/pages/manager_page/list_booking.dart
@@ -10,7 +10,6 @@ import 'package:titan/booking/providers/manager_confirmed_booking_list_provider.
import 'package:titan/booking/providers/user_booking_list_provider.dart';
import 'package:titan/booking/providers/selected_days_provider.dart';
import 'package:titan/booking/router.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/components/booking_card.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
@@ -18,6 +17,7 @@ import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:qlevar_router/qlevar_router.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ListBooking extends HookConsumerWidget {
final List bookings;
@@ -119,8 +119,10 @@ class ListBooking extends HookConsumerWidget {
context: context,
builder: (context) {
return CustomDialogBox(
- title: BookingTextConstants.confirm,
- descriptions: BookingTextConstants.confirmBooking,
+ title: AppLocalizations.of(context)!.bookingConfirm,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.bookingConfirmBooking,
onYes: () async {
await tokenExpireWrapper(ref, () async {
Booking newBooking = e.copyWith(
@@ -156,8 +158,10 @@ class ListBooking extends HookConsumerWidget {
context: context,
builder: (context) {
return CustomDialogBox(
- title: BookingTextConstants.decline,
- descriptions: BookingTextConstants.declineBooking,
+ title: AppLocalizations.of(context)!.bookingDecline,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.bookingDeclineBooking,
onYes: () async {
await tokenExpireWrapper(ref, () async {
Booking newBooking = e.copyWith(
diff --git a/lib/booking/ui/pages/manager_page/manager_page.dart b/lib/booking/ui/pages/manager_page/manager_page.dart
index b10f5ac4e0..b272f143ec 100644
--- a/lib/booking/ui/pages/manager_page/manager_page.dart
+++ b/lib/booking/ui/pages/manager_page/manager_page.dart
@@ -4,12 +4,12 @@ import 'package:titan/booking/class/booking.dart';
import 'package:titan/booking/providers/manager_booking_list_provider.dart';
import 'package:titan/booking/providers/manager_confirmed_booking_list_provider.dart';
import 'package:titan/service/providers/room_list_provider.dart';
-import 'package:titan/booking/tools/constants.dart';
import 'package:titan/booking/ui/booking.dart';
import 'package:titan/booking/ui/calendar/calendar.dart';
import 'package:titan/booking/ui/pages/manager_page/list_booking.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ManagerPage extends HookConsumerWidget {
const ManagerPage({super.key});
@@ -43,6 +43,7 @@ class ManagerPage extends HookConsumerWidget {
);
return BookingTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await ref
.watch(managerBookingListProvider.notifier)
@@ -63,10 +64,10 @@ class ManagerPage extends HookConsumerWidget {
if (pendingBookings.isEmpty &&
confirmedBookings.isEmpty &&
canceledBookings.isEmpty)
- const Center(
+ Center(
child: Text(
- BookingTextConstants.noCurrentBooking,
- style: TextStyle(
+ AppLocalizations.of(context)!.bookingNoCurrentBooking,
+ style: const TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold,
@@ -74,16 +75,16 @@ class ManagerPage extends HookConsumerWidget {
),
),
ListBooking(
- title: BookingTextConstants.pending,
+ title: AppLocalizations.of(context)!.bookingPending,
bookings: pendingBookings,
canToggle: false,
),
ListBooking(
- title: BookingTextConstants.confirmed,
+ title: AppLocalizations.of(context)!.bookingConfirmed,
bookings: confirmedBookings,
),
ListBooking(
- title: BookingTextConstants.declined,
+ title: AppLocalizations.of(context)!.bookingDeclined,
bookings: canceledBookings,
),
const SizedBox(height: 30),
diff --git a/lib/centralisation/router.dart b/lib/centralisation/router.dart
index 160e528a33..69d90b9d67 100644
--- a/lib/centralisation/router.dart
+++ b/lib/centralisation/router.dart
@@ -1,10 +1,9 @@
-import 'package:either_dart/either.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:titan/centralisation/tools/constants.dart';
import 'package:titan/centralisation/ui/pages/main_page.dart'
deferred as main_page;
-import 'package:titan/drawer/class/module.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
import 'package:titan/tools/middlewares/authenticated_middleware.dart';
import 'package:titan/tools/middlewares/deferred_middleware.dart';
import 'package:qlevar_router/qlevar_router.dart';
@@ -13,10 +12,10 @@ class CentralisationRouter {
final Ref ref;
static const String root = '/centralisation';
static final Module module = Module(
- name: CentralisationTextConstants.centralisation,
- icon: const Left(HeroIcons.link),
+ getName: (context) => AppLocalizations.of(context)!.moduleCentralisation,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleCentralisationDescription,
root: CentralisationRouter.root,
- selected: false,
);
CentralisationRouter(this.ref);
@@ -28,5 +27,9 @@ class CentralisationRouter {
AuthenticatedMiddleware(ref),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
);
}
diff --git a/lib/centralisation/ui/centralisation.dart b/lib/centralisation/ui/centralisation.dart
index 3cba972886..21faa3339d 100644
--- a/lib/centralisation/ui/centralisation.dart
+++ b/lib/centralisation/ui/centralisation.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:titan/centralisation/router.dart';
-import 'package:titan/centralisation/tools/constants.dart';
import 'package:titan/tools/ui/widgets/top_bar.dart';
+import 'package:titan/tools/constants.dart';
class CentralisationTemplate extends StatelessWidget {
final Widget child;
@@ -11,15 +11,12 @@ class CentralisationTemplate extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Container(
- color: Colors.white,
+ color: ColorConstants.background,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
- const TopBar(
- title: CentralisationTextConstants.centralisation,
- root: CentralisationRouter.root,
- ),
+ const TopBar(root: CentralisationRouter.root),
Expanded(child: child),
],
),
diff --git a/lib/cinema/providers/is_cinema_admin.dart b/lib/cinema/providers/is_cinema_admin.dart
index 5a15b340c5..0e8f1e5afb 100644
--- a/lib/cinema/providers/is_cinema_admin.dart
+++ b/lib/cinema/providers/is_cinema_admin.dart
@@ -5,5 +5,5 @@ final isCinemaAdminProvider = StateProvider((ref) {
final me = ref.watch(userProvider);
return me.groups
.map((e) => e.id)
- .contains("ce5f36e6-5377-489f-9696-de70e2477300");
+ .contains("ce5f36e6-5377-489f-9696-de70e2477300"); // admin_cinema
});
diff --git a/lib/cinema/router.dart b/lib/cinema/router.dart
index 01108fcdee..be9230bbd6 100644
--- a/lib/cinema/router.dart
+++ b/lib/cinema/router.dart
@@ -1,6 +1,5 @@
-import 'package:either_dart/either.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
import 'package:titan/cinema/providers/is_cinema_admin.dart';
import 'package:titan/cinema/ui/pages/admin_page/admin_page.dart'
deferred as admin_page;
@@ -10,7 +9,8 @@ import 'package:titan/cinema/ui/pages/main_page/main_page.dart'
deferred as main_page;
import 'package:titan/cinema/ui/pages/session_pages/add_edit_session.dart'
deferred as add_edit_session_page;
-import 'package:titan/drawer/class/module.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
import 'package:titan/tools/middlewares/admin_middleware.dart';
import 'package:titan/tools/middlewares/authenticated_middleware.dart';
import 'package:titan/tools/middlewares/deferred_middleware.dart';
@@ -24,10 +24,10 @@ class CinemaRouter {
static const String addEdit = '/add_edit';
static const String detail = '/detail';
static final Module module = Module(
- name: "Cinéma",
- icon: const Left(HeroIcons.ticket),
+ getName: (context) => AppLocalizations.of(context)!.moduleCinema,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleCinemaDescription,
root: CinemaRouter.root,
- selected: false,
);
CinemaRouter(this.ref);
@@ -40,6 +40,10 @@ class CinemaRouter {
NotificationMiddleWare(ref),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
children: [
QRoute(
path: detail,
diff --git a/lib/cinema/tools/constants.dart b/lib/cinema/tools/constants.dart
index 933f612c0b..2b625d283a 100644
--- a/lib/cinema/tools/constants.dart
+++ b/lib/cinema/tools/constants.dart
@@ -3,37 +3,3 @@ import 'package:flutter/material.dart';
class CinemaColorConstants {
static const Color tmdbColor = Color(0xffe2b616);
}
-
-class CinemaTextConstants {
- static const String add = "Ajouter";
- static const String addedSession = "Séance ajoutée";
- static const String addingError = "Erreur lors de l'ajout";
- static const String addSession = "Ajouter une séance";
- static const String cinema = "Cinéma";
- static const String deleteSession = "Supprimer la séance ?";
- static const String deleting = "Suppression";
- static const String duration = "Durée";
- static const String edit = "Modifier";
- static const String editedSession = "Séance modifiée";
- static const String editingError = "Erreur lors de la modification";
- static const String editSession = "Modifier la séance";
- static const String emptyUrl = "Veuillez entrer une URL";
- static const String importFromTMDB = "Importer depuis TMDB";
- static const String incomingSession = "A l'affiche";
- static const String incorrectOrMissingFields =
- "Champs incorrects ou manquants";
- static const String invalidUrl = "URL invalide";
- static const String genre = "Genre";
- static const String name = "Nom";
- static const String noDateError = "Veuillez entrer une date";
- static const String noDuration = "Veuillez entrer une durée";
- static const String noOverview = "Aucun synopsis";
- static const String noPoster = "Aucune affiche";
- static const String noSession = "Aucune séance";
- static const String overview = "Synopsis";
- static const String posterUrl = "URL de l'affiche";
- static const String sessionDate = "Jour de la séance";
- static const String startHour = "Heure de début";
- static const String tagline = "Slogan";
- static const String the = "Le";
-}
diff --git a/lib/cinema/ui/cinema.dart b/lib/cinema/ui/cinema.dart
index 6376ce34c6..5d6c4f3f83 100644
--- a/lib/cinema/ui/cinema.dart
+++ b/lib/cinema/ui/cinema.dart
@@ -1,9 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/cinema/providers/main_page_index_provider.dart';
-import 'package:titan/cinema/providers/scroll_provider.dart';
import 'package:titan/cinema/router.dart';
-import 'package:titan/cinema/tools/constants.dart';
+import 'package:titan/tools/constants.dart';
import 'package:titan/tools/ui/widgets/top_bar.dart';
class CinemaTemplate extends HookConsumerWidget {
@@ -12,22 +10,16 @@ class CinemaTemplate extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
- final initialPageNotifier = ref.watch(mainPageIndexProvider.notifier);
- final scrollNotifier = ref.watch(scrollProvider.notifier);
- return SafeArea(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- TopBar(
- title: CinemaTextConstants.cinema,
- root: CinemaRouter.root,
- onMenu: () {
- initialPageNotifier.reset();
- scrollNotifier.reset();
- },
- ),
- Expanded(child: child),
- ],
+ return Container(
+ color: ColorConstants.background,
+ child: SafeArea(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ TopBar(root: CinemaRouter.root),
+ Expanded(child: child),
+ ],
+ ),
),
);
}
diff --git a/lib/cinema/ui/pages/admin_page/admin_page.dart b/lib/cinema/ui/pages/admin_page/admin_page.dart
index e32adf28e7..46718e86ff 100644
--- a/lib/cinema/ui/pages/admin_page/admin_page.dart
+++ b/lib/cinema/ui/pages/admin_page/admin_page.dart
@@ -5,13 +5,13 @@ import 'package:titan/cinema/class/session.dart';
import 'package:titan/cinema/providers/session_list_provider.dart';
import 'package:titan/cinema/providers/session_provider.dart';
import 'package:titan/cinema/router.dart';
-import 'package:titan/cinema/tools/constants.dart';
import 'package:titan/cinema/ui/cinema.dart';
import 'package:titan/cinema/ui/pages/admin_page/admin_session_card.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AdminPage extends HookConsumerWidget {
const AdminPage({super.key});
@@ -60,8 +60,10 @@ class AdminPage extends HookConsumerWidget {
context: context,
builder: (context) {
return CustomDialogBox(
- title: CinemaTextConstants.deleting,
- descriptions: CinemaTextConstants.deleteSession,
+ title: AppLocalizations.of(context)!.cinemaDeleting,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.cinemaDeleteSession,
onYes: () {
sessionListNotifier.deleteSession(session);
},
diff --git a/lib/cinema/ui/pages/detail_page/detail_page.dart b/lib/cinema/ui/pages/detail_page/detail_page.dart
index b9d8976307..d9e959e64e 100644
--- a/lib/cinema/ui/pages/detail_page/detail_page.dart
+++ b/lib/cinema/ui/pages/detail_page/detail_page.dart
@@ -9,7 +9,6 @@ import 'package:titan/cinema/providers/cinema_topic_provider.dart';
import 'package:titan/cinema/providers/session_poster_map_provider.dart';
import 'package:titan/cinema/providers/session_poster_provider.dart';
import 'package:titan/cinema/providers/session_provider.dart';
-import 'package:titan/cinema/tools/constants.dart';
import 'package:titan/cinema/tools/functions.dart';
import 'package:titan/service/class/message.dart';
import 'package:titan/service/local_notification_service.dart';
@@ -17,6 +16,7 @@ import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/builders/auto_loader_child.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DetailPage extends HookConsumerWidget {
const DetailPage({super.key});
@@ -153,7 +153,7 @@ class DetailPage extends HookConsumerWidget {
child: Text(
session.overview != null
? session.overview!
- : CinemaTextConstants.noOverview,
+ : AppLocalizations.of(context)!.cinemaNoOverview,
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15),
),
diff --git a/lib/cinema/ui/pages/main_page/main_page.dart b/lib/cinema/ui/pages/main_page/main_page.dart
index 1423fe525d..5f345e4551 100644
--- a/lib/cinema/ui/pages/main_page/main_page.dart
+++ b/lib/cinema/ui/pages/main_page/main_page.dart
@@ -9,14 +9,14 @@ import 'package:titan/cinema/providers/session_list_provider.dart';
import 'package:titan/cinema/providers/session_poster_map_provider.dart';
import 'package:titan/cinema/providers/session_provider.dart';
import 'package:titan/cinema/router.dart';
-import 'package:titan/cinema/tools/constants.dart';
import 'package:titan/cinema/ui/cinema.dart';
import 'package:titan/cinema/ui/pages/main_page/session_card.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
+import 'package:titan/navigation/providers/is_web_format_provider.dart';
import 'package:titan/tools/ui/widgets/admin_button.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class CinemaMainPage extends HookConsumerWidget {
const CinemaMainPage({super.key});
@@ -45,6 +45,7 @@ class CinemaMainPage extends HookConsumerWidget {
return CinemaTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await sessionListNotifier.loadSessions();
ref.watch(mainPageIndexProvider.notifier).reset();
@@ -63,9 +64,9 @@ class CinemaMainPage extends HookConsumerWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- const Text(
- CinemaTextConstants.incomingSession,
- style: TextStyle(
+ Text(
+ AppLocalizations.of(context)!.cinemaIncomingSession,
+ style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey,
@@ -88,12 +89,12 @@ class CinemaMainPage extends HookConsumerWidget {
builder: (context, data) {
data.sort((a, b) => a.start.compareTo(b.start));
if (data.isEmpty) {
- return const SizedBox(
+ return SizedBox(
height: 200,
child: Center(
child: Text(
- CinemaTextConstants.noSession,
- style: TextStyle(
+ AppLocalizations.of(context)!.cinemaNoSession,
+ style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
diff --git a/lib/cinema/ui/pages/main_page/session_card.dart b/lib/cinema/ui/pages/main_page/session_card.dart
index c1513a9ad0..afa5f5eb9d 100644
--- a/lib/cinema/ui/pages/main_page/session_card.dart
+++ b/lib/cinema/ui/pages/main_page/session_card.dart
@@ -6,10 +6,10 @@ import 'package:titan/cinema/providers/cinema_topic_provider.dart';
import 'package:titan/cinema/providers/scroll_provider.dart';
import 'package:titan/cinema/providers/session_poster_map_provider.dart';
import 'package:titan/cinema/providers/session_poster_provider.dart';
-import 'package:titan/cinema/tools/constants.dart';
import 'package:titan/cinema/tools/functions.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
+import 'package:titan/navigation/providers/is_web_format_provider.dart';
import 'package:titan/tools/ui/builders/auto_loader_child.dart';
+import 'package:titan/l10n/app_localizations.dart';
class SessionCard extends HookConsumerWidget {
final Session session;
@@ -164,7 +164,9 @@ class SessionCard extends HookConsumerWidget {
const SizedBox(height: 10),
Text(
session.overview ??
- CinemaTextConstants.noOverview,
+ AppLocalizations.of(
+ context,
+ )!.cinemaNoOverview,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
diff --git a/lib/cinema/ui/pages/session_pages/add_edit_session.dart b/lib/cinema/ui/pages/session_pages/add_edit_session.dart
index 86e350a3ec..23026ebd99 100644
--- a/lib/cinema/ui/pages/session_pages/add_edit_session.dart
+++ b/lib/cinema/ui/pages/session_pages/add_edit_session.dart
@@ -4,13 +4,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/cinema/class/session.dart';
import 'package:titan/cinema/providers/session_list_provider.dart';
import 'package:titan/cinema/providers/session_poster_map_provider.dart';
import 'package:titan/cinema/providers/session_poster_provider.dart';
import 'package:titan/cinema/providers/session_provider.dart';
import 'package:titan/cinema/providers/the_movie_db_genre_provider.dart';
-import 'package:titan/cinema/tools/constants.dart';
import 'package:titan/cinema/tools/functions.dart';
import 'package:titan/cinema/ui/cinema.dart';
import 'package:titan/cinema/ui/pages/session_pages/tmdb_button.dart';
@@ -22,12 +22,14 @@ import 'package:titan/tools/ui/widgets/date_entry.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:titan/tools/ui/widgets/text_entry.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditSessionPage extends HookConsumerWidget {
const AddEditSessionPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final session = ref.watch(sessionProvider);
final movieNotifier = ref.watch(theMovieDBMovieProvider.notifier);
final isEdit = session.id != Session.empty().id;
@@ -42,7 +44,7 @@ class AddEditSessionPage extends HookConsumerWidget {
final genre = useTextEditingController(text: session.genre ?? '');
final overview = useTextEditingController(text: session.overview ?? '');
final start = useTextEditingController(
- text: isEdit ? processDateWithHour(session.start) : '',
+ text: isEdit ? DateFormat.yMd(locale).add_Hm().format(session.start) : '',
);
final tagline = useTextEditingController(text: session.tagline ?? '');
final sessionPosterMap = ref.watch(sessionPosterMapProvider);
@@ -74,8 +76,8 @@ class AddEditSessionPage extends HookConsumerWidget {
children: [
AlignLeftText(
isEdit
- ? CinemaTextConstants.editSession
- : CinemaTextConstants.addSession,
+ ? AppLocalizations.of(context)!.cinemaEditSession
+ : AppLocalizations.of(context)!.cinemaAddSession,
color: Colors.grey,
),
const SizedBox(height: 30),
@@ -83,7 +85,9 @@ class AddEditSessionPage extends HookConsumerWidget {
controller: tmdbUrl,
cursorColor: Colors.black,
decoration: InputDecoration(
- labelText: CinemaTextConstants.importFromTMDB,
+ labelText: AppLocalizations.of(
+ context,
+ )!.cinemaImportFromTMDB,
labelStyle: const TextStyle(
color: Colors.black,
fontSize: 20,
@@ -95,7 +99,7 @@ class AddEditSessionPage extends HookConsumerWidget {
if (tmdbUrl.text.isEmpty) {
displayToastWithContext(
TypeMsg.error,
- CinemaTextConstants.emptyUrl,
+ AppLocalizations.of(context)!.cinemaEmptyUrl,
);
return;
}
@@ -135,7 +139,7 @@ class AddEditSessionPage extends HookConsumerWidget {
} on FormatException catch (_) {
displayToastWithContext(
TypeMsg.error,
- CinemaTextConstants.invalidUrl,
+ AppLocalizations.of(context)!.cinemaInvalidUrl,
);
return;
}
@@ -183,10 +187,13 @@ class AddEditSessionPage extends HookConsumerWidget {
)
: Image.memory(logo.value!, fit: BoxFit.cover),
const SizedBox(height: 30),
- TextEntry(label: CinemaTextConstants.name, controller: name),
+ TextEntry(
+ label: AppLocalizations.of(context)!.cinemaName,
+ controller: name,
+ ),
const SizedBox(height: 30),
TextEntry(
- label: CinemaTextConstants.posterUrl,
+ label: AppLocalizations.of(context)!.cinemaPosterUrl,
controller: posterUrl,
onChanged: (value) async {
logo.value = await getFromUrl(posterUrl.text);
@@ -196,30 +203,30 @@ class AddEditSessionPage extends HookConsumerWidget {
const SizedBox(height: 30),
DateEntry(
onTap: () => getFullDate(context, start),
- label: CinemaTextConstants.sessionDate,
+ label: AppLocalizations.of(context)!.cinemaSessionDate,
controller: start,
),
const SizedBox(height: 30),
DateEntry(
onTap: () => getOnlyHourDate(context, duration),
- label: CinemaTextConstants.duration,
+ label: AppLocalizations.of(context)!.cinemaDuration,
controller: duration,
),
const SizedBox(height: 30),
TextEntry(
- label: CinemaTextConstants.genre,
+ label: AppLocalizations.of(context)!.cinemaGenre,
controller: genre,
canBeEmpty: true,
),
const SizedBox(height: 30),
TextEntry(
- label: CinemaTextConstants.overview,
+ label: AppLocalizations.of(context)!.cinemaOverview,
controller: overview,
canBeEmpty: true,
),
const SizedBox(height: 30),
TextEntry(
- label: CinemaTextConstants.tagline,
+ label: AppLocalizations.of(context)!.cinemaTagline,
controller: tagline,
canBeEmpty: true,
),
@@ -230,11 +237,23 @@ class AddEditSessionPage extends HookConsumerWidget {
if (key.currentState == null) {
return;
}
+ final editedSessionMsg = AppLocalizations.of(
+ context,
+ )!.cinemaEditedSession;
+ final addedSessionMsg = AppLocalizations.of(
+ context,
+ )!.cinemaAddedSession;
+ final editingErrorMsg = AppLocalizations.of(
+ context,
+ )!.cinemaEditingError;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.cinemaAddingError;
if (key.currentState!.validate()) {
if (logo.value == null && logoFile.value == null) {
displayToastWithContext(
TypeMsg.error,
- CinemaTextConstants.noPoster,
+ AppLocalizations.of(context)!.cinemaNoPoster,
);
return;
}
@@ -248,7 +267,10 @@ class AddEditSessionPage extends HookConsumerWidget {
? null
: overview.text,
start: DateTime.parse(
- processDateBackWithHour(start.text),
+ processDateBackWithHour(
+ start.text,
+ locale.toString(),
+ ),
),
tagline: tagline.text.isEmpty ? null : tagline.text,
);
@@ -279,7 +301,7 @@ class AddEditSessionPage extends HookConsumerWidget {
);
displayToastWithContext(
TypeMsg.msg,
- CinemaTextConstants.editedSession,
+ editedSessionMsg,
);
} else {
sessionList.maybeWhen(
@@ -302,19 +324,19 @@ class AddEditSessionPage extends HookConsumerWidget {
);
displayToastWithContext(
TypeMsg.msg,
- CinemaTextConstants.addedSession,
+ addedSessionMsg,
);
}
} else {
if (isEdit) {
displayToastWithContext(
TypeMsg.error,
- CinemaTextConstants.editingError,
+ editingErrorMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- CinemaTextConstants.addingError,
+ addingErrorMsg,
);
}
}
@@ -323,12 +345,16 @@ class AddEditSessionPage extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- CinemaTextConstants.incorrectOrMissingFields,
+ AppLocalizations.of(
+ context,
+ )!.cinemaIncorrectOrMissingFields,
);
}
},
child: Text(
- isEdit ? CinemaTextConstants.edit : CinemaTextConstants.add,
+ isEdit
+ ? AppLocalizations.of(context)!.cinemaEdit
+ : AppLocalizations.of(context)!.cinemaAdd,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
diff --git a/lib/drawer/class/module.dart b/lib/drawer/class/module.dart
deleted file mode 100644
index d32bcec9b8..0000000000
--- a/lib/drawer/class/module.dart
+++ /dev/null
@@ -1,56 +0,0 @@
-import 'package:either_dart/either.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_svg/flutter_svg.dart';
-import 'package:heroicons/heroicons.dart';
-
-enum ModuleType {
- calendar,
- settings,
- amap,
- loan,
- booking,
- admin,
- event,
- vote,
- tombola,
- cinema,
- paiement,
-}
-
-class Module {
- String name;
- Either icon;
- String root;
- bool selected;
-
- Module({
- required this.name,
- required this.icon,
- required this.root,
- required this.selected,
- });
-
- Module copy({
- String? name,
- Either? icon,
- String? root,
- bool? selected,
- }) => Module(
- name: name ?? this.name,
- icon: icon ?? this.icon,
- root: root ?? this.root,
- selected: selected ?? this.selected,
- );
-
- Widget getIcon(Color color, {double size = 30}) {
- return icon.fold(
- (heroIcon) => HeroIcon(heroIcon, color: color, size: size),
- (svgPath) => SvgPicture.asset(
- svgPath,
- width: size,
- height: size,
- colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
- ),
- );
- }
-}
diff --git a/lib/drawer/class/top_bar_callback.dart b/lib/drawer/class/top_bar_callback.dart
deleted file mode 100644
index 09f0a59bcf..0000000000
--- a/lib/drawer/class/top_bar_callback.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-import 'package:flutter/material.dart';
-
-class TopBarCallback {
- final String moduleRoot;
- final VoidCallback? onMenu;
- final VoidCallback? onBack;
-
- TopBarCallback({this.onMenu, this.onBack, required this.moduleRoot});
-}
diff --git a/lib/drawer/providers/already_displayed_popup.dart b/lib/drawer/providers/already_displayed_popup.dart
deleted file mode 100644
index 67393b6838..0000000000
--- a/lib/drawer/providers/already_displayed_popup.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-
-class AlreadyDisplayedNotifier extends StateNotifier {
- AlreadyDisplayedNotifier() : super(false);
-
- void setAlreadyDisplayed() {
- state = true;
- }
-}
-
-final alreadyDisplayedProvider =
- StateNotifierProvider((ref) {
- return AlreadyDisplayedNotifier();
- });
diff --git a/lib/drawer/providers/modules_provider.dart b/lib/drawer/providers/modules_provider.dart
deleted file mode 100644
index 04e61ae8aa..0000000000
--- a/lib/drawer/providers/modules_provider.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:titan/drawer/class/module.dart';
-import 'package:titan/settings/providers/module_list_provider.dart';
-
-class ModuleListNotifier extends StateNotifier> {
- ModuleListNotifier(super.listModule);
-
- void select(int i) {
- List r = state.sublist(0);
-
- for (int j = 0; j < r.length; j++) {
- if (i == j) {
- r[i].selected = true;
- } else {
- r[j].selected = false;
- }
- }
-
- state = r;
- }
-}
-
-final listModuleProvider =
- StateNotifierProvider>((ref) {
- final modules = ref.watch(modulesProvider);
- return ModuleListNotifier(modules);
- });
diff --git a/lib/drawer/providers/swipe_provider.dart b/lib/drawer/providers/swipe_provider.dart
deleted file mode 100644
index b4e79beba6..0000000000
--- a/lib/drawer/providers/swipe_provider.dart
+++ /dev/null
@@ -1,66 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-
-class SwipeControllerNotifier extends StateNotifier {
- SwipeControllerNotifier(super.controller);
-
- static const double maxSlide = 255;
- static const dragRightStartVal = 60;
- static const dragLeftStartVal = maxSlide - 30;
- static bool shouldDrag = false;
-
- void close() {
- state.reverse();
- }
-
- void open() {
- state.forward();
- }
-
- void toggle() {
- if (state.isCompleted) {
- close();
- } else {
- open();
- }
- }
-
- void onDragStart(DragStartDetails startDetails) {
- bool isDraggingFromLeft =
- state.isDismissed && startDetails.globalPosition.dx < dragRightStartVal;
- bool isDraggingFromRight =
- !state.isDismissed && startDetails.globalPosition.dx > dragLeftStartVal;
- shouldDrag = isDraggingFromLeft || isDraggingFromRight;
- }
-
- void onDragUpdate(DragUpdateDetails updateDetails) {
- if (shouldDrag) {
- double delta = updateDetails.primaryDelta! / maxSlide;
- state.value += delta;
- }
- }
-
- void onDragEnd(DragEndDetails endDetails, double width) {
- if (!state.isDismissed && !state.isCompleted) {
- double minFlingVelocity = 365.0;
- double dragVelocity = endDetails.velocity.pixelsPerSecond.dx.abs();
- if (dragVelocity >= minFlingVelocity) {
- double visualVelocity = endDetails.velocity.pixelsPerSecond.dx / width;
- state.fling(velocity: visualVelocity);
- } else if (state.value < 0.5) {
- close();
- } else {
- open();
- }
- }
- }
-}
-
-final swipeControllerProvider =
- StateNotifierProvider.family<
- SwipeControllerNotifier,
- AnimationController,
- AnimationController
- >((ref, animationController) {
- return SwipeControllerNotifier(animationController);
- });
diff --git a/lib/drawer/providers/top_bar_callback_provider.dart b/lib/drawer/providers/top_bar_callback_provider.dart
deleted file mode 100644
index a4f7644f6b..0000000000
--- a/lib/drawer/providers/top_bar_callback_provider.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:titan/drawer/class/top_bar_callback.dart';
-
-class AnimationNotifier extends StateNotifier {
- AnimationNotifier() : super(TopBarCallback(moduleRoot: ''));
-
- void setCallBacks(TopBarCallback callbacks) {
- if (state.moduleRoot == callbacks.moduleRoot) {
- return;
- }
- state = callbacks;
- }
-}
-
-final topBarCallBackProvider =
- StateNotifierProvider((ref) {
- return AnimationNotifier();
- });
diff --git a/lib/drawer/tools/constants.dart b/lib/drawer/tools/constants.dart
deleted file mode 100644
index d488510137..0000000000
--- a/lib/drawer/tools/constants.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-import 'package:flutter/material.dart';
-
-class DrawerColorConstants {
- static final Color lightText = Colors.grey.shade100.withValues(alpha: 0.6);
- static final Color selectedText = Colors.grey.shade100;
- static const Color lightBlue = Color.fromARGB(255, 86, 95, 95);
- static const Color darkBlue = Color.fromARGB(255, 62, 62, 62);
- static const Color fakePageBlue = Color.fromARGB(24, 161, 161, 161);
- static const Color fakePageShadow = Color.fromARGB(14, 161, 161, 161);
-}
-
-class DrawerTextConstants {
- static const String admin = "Administration";
- static const String androidAppLink =
- "https://play.google.com/store/apps/details?id=fr.myecl.titan";
- static const String copied = "Copié !";
- static const String downloadAppOnMobileDevice =
- "Ce site est la version Web de l'application MyECL. Nous vous invitons à télécharger l'application. N'utilisez ce site qu'en cas de problème avec l'application.\n";
- static const String iosAppLink =
- "https://apps.apple.com/fr/app/myecl/id6444443430";
- static const String loginOut = "Voulez-vous vous déconnecter ?";
- static const String logOut = "Déconnexion";
- static const String or = " ou ";
- static const String settings = "Paramètres";
-}
diff --git a/lib/drawer/ui/bottom_bar.dart b/lib/drawer/ui/bottom_bar.dart
deleted file mode 100644
index 94d74e5942..0000000000
--- a/lib/drawer/ui/bottom_bar.dart
+++ /dev/null
@@ -1,52 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:titan/drawer/providers/display_quit_popup.dart';
-import 'package:titan/drawer/tools/constants.dart';
-
-class BottomBar extends ConsumerWidget {
- const BottomBar({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final displayQuitNotifier = ref.watch(displayQuitProvider.notifier);
- return Column(
- children: [
- SizedBox(
- height: 30,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- GestureDetector(
- behavior: HitTestBehavior.translucent,
- onTap: () {
- displayQuitNotifier.setDisplay(true);
- },
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- const SizedBox(width: 25),
- HeroIcon(
- HeroIcons.arrowRightOnRectangle,
- color: DrawerColorConstants.lightText,
- size: 27,
- ),
- const SizedBox(width: 15),
- Text(
- DrawerTextConstants.logOut,
- style: TextStyle(
- color: DrawerColorConstants.lightText,
- fontSize: 18,
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- const SizedBox(height: 30),
- ],
- );
- }
-}
diff --git a/lib/drawer/ui/custom_drawer.dart b/lib/drawer/ui/custom_drawer.dart
deleted file mode 100644
index 6fd148196b..0000000000
--- a/lib/drawer/ui/custom_drawer.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
-import 'package:titan/drawer/tools/constants.dart';
-import 'package:titan/drawer/ui/bottom_bar.dart';
-import 'package:titan/drawer/ui/fake_page.dart';
-import 'package:titan/drawer/ui/list_module.dart';
-import 'package:titan/drawer/ui/drawer_top_bar.dart';
-
-class CustomDrawer extends HookConsumerWidget {
- const CustomDrawer({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final isWebFormat = ref.watch(isWebFormatProvider);
- return Container(
- decoration: const BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- colors: [
- DrawerColorConstants.lightBlue,
- DrawerColorConstants.darkBlue,
- ],
- ),
- ),
- child: SafeArea(
- child: Stack(
- children: [
- const Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [DrawerTopBar(), BottomBar()],
- ),
- Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Expanded(
- child: SizedBox(
- width: 200,
- height: MediaQuery.of(context).size.height * 4.4 / 10,
- child: const ListModule(),
- ),
- ),
- isWebFormat
- ? Container(
- width: MediaQuery.of(context).size.width - 220,
- )
- : const FakePage(),
- ],
- ),
- ],
- ),
- ],
- ),
- ),
- );
- }
-}
diff --git a/lib/drawer/ui/drawer_template.dart b/lib/drawer/ui/drawer_template.dart
deleted file mode 100644
index eaa11ebe05..0000000000
--- a/lib/drawer/ui/drawer_template.dart
+++ /dev/null
@@ -1,136 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:flutter/foundation.dart' show kIsWeb;
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/auth/providers/openid_provider.dart';
-import 'package:titan/drawer/providers/animation_provider.dart';
-import 'package:titan/drawer/providers/display_quit_popup.dart';
-import 'package:titan/drawer/providers/is_web_format_provider.dart';
-import 'package:titan/drawer/providers/should_setup_provider.dart';
-import 'package:titan/drawer/providers/swipe_provider.dart';
-import 'package:titan/drawer/ui/custom_drawer.dart';
-import 'package:titan/service/tools/setup.dart';
-import 'package:titan/drawer/providers/already_displayed_popup.dart';
-import 'package:titan/drawer/ui/quit_dialog.dart';
-import 'package:titan/drawer/ui/email_change_popup.dart';
-import 'package:titan/tools/providers/should_notify_provider.dart';
-import 'package:titan/user/providers/user_provider.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class DrawerTemplate extends HookConsumerWidget {
- static Duration duration = const Duration(milliseconds: 200);
- static const double maxSlide = 255;
- static const dragRightStartVal = 60;
- static const dragLeftStartVal = maxSlide - 20;
- static bool shouldDrag = false;
- final Widget child;
-
- const DrawerTemplate({super.key, required this.child});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- // We are logged in, so we can set up the notification
- final user = ref.watch(userProvider);
- final animationController = useAnimationController(
- duration: duration,
- initialValue: 1,
- );
- final animationNotifier = ref.read(animationProvider.notifier);
- final controller = ref.watch(swipeControllerProvider(animationController));
- final controllerNotifier = ref.watch(
- swipeControllerProvider(animationController).notifier,
- );
- final isWebFormat = ref.watch(isWebFormatProvider);
- final alreadyDisplayed = ref.watch(alreadyDisplayedProvider);
- final displayQuit = ref.watch(displayQuitProvider);
- final shouldNotify = ref.watch(shouldNotifyProvider);
- final isLoggedIn = ref.watch(isLoggedInProvider);
- final shouldSetup = ref.watch(shouldSetupProvider);
- final shouldSetupNotifier = ref.read(shouldSetupProvider.notifier);
- if (isWebFormat) {
- controllerNotifier.close();
- }
-
- Future(() {
- animationNotifier.setController(animationController);
- if (!kIsWeb && user.id != "" && shouldSetup) {
- setUpNotification(ref);
- shouldSetupNotifier.setShouldSetup();
- }
- });
-
- return Scaffold(
- body: Stack(
- children: [
- GestureDetector(
- onHorizontalDragStart: controllerNotifier.onDragStart,
- onHorizontalDragUpdate: controllerNotifier.onDragUpdate,
- onHorizontalDragEnd: (details) => controllerNotifier.onDragEnd(
- details,
- MediaQuery.of(context).size.width,
- ),
- onTap: () {},
- child: AnimatedBuilder(
- animation: controller,
- builder: (BuildContext context, _) {
- double animationVal = controller.value;
- double translateVal = animationVal * maxSlide;
- double scaleVal = 1 - (isWebFormat ? 0 : (animationVal * 0.3));
- double cornerVal = isWebFormat ? 0 : 30.0 * animationVal;
- return Stack(
- children: [
- const CustomDrawer(),
- Transform(
- alignment: Alignment.centerLeft,
- transform: Matrix4.identity()
- ..translate(translateVal)
- ..scale(scaleVal),
- child: GestureDetector(
- onTap: () {
- if (controller.isCompleted) {
- controllerNotifier.close();
- }
- },
- child: ClipRRect(
- borderRadius: BorderRadius.circular(cornerVal),
- child: Stack(
- children: [
- Container(
- color: Colors.white,
- child: IgnorePointer(
- ignoring: controller.isCompleted,
- child: child,
- ),
- ),
- MouseRegion(
- cursor: SystemMouseCursors.click,
- onEnter: (event) {
- controllerNotifier.toggle();
- },
- child: Container(
- color: Colors.transparent,
- width: 20,
- height: double.infinity,
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- ],
- );
- },
- ),
- ),
- if (isLoggedIn &&
- shouldNotify &&
- QR.context != null &&
- !alreadyDisplayed)
- const EmailChangeDialog(),
- if (displayQuit) const QuitDialog(),
- ],
- ),
- );
- }
-}
diff --git a/lib/drawer/ui/drawer_top_bar.dart b/lib/drawer/ui/drawer_top_bar.dart
deleted file mode 100644
index 2c9447b638..0000000000
--- a/lib/drawer/ui/drawer_top_bar.dart
+++ /dev/null
@@ -1,279 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/admin/providers/is_admin_provider.dart';
-import 'package:titan/admin/router.dart';
-import 'package:titan/auth/providers/is_connected_provider.dart';
-import 'package:titan/drawer/providers/animation_provider.dart';
-import 'package:titan/drawer/providers/swipe_provider.dart';
-import 'package:titan/drawer/tools/constants.dart';
-import 'package:titan/home/providers/scrolled_provider.dart';
-import 'package:titan/settings/router.dart';
-import 'package:titan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/providers/path_forwarding_provider.dart';
-import 'package:titan/tools/ui/builders/async_child.dart';
-import 'package:titan/user/providers/user_provider.dart';
-import 'package:titan/user/providers/profile_picture_provider.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class DrawerTopBar extends HookConsumerWidget {
- const DrawerTopBar({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final pathForwardingNotifier = ref.read(pathForwardingProvider.notifier);
- final pathForwarding = ref.watch(pathForwardingProvider);
- final user = ref.watch(userProvider);
- final profilePicture = ref.watch(profilePictureProvider);
- final hasScrolled = ref.watch(hasScrolledProvider.notifier);
- final isAdmin = ref.watch(isAdminProvider);
- final isConnected = ref.watch(isConnectedProvider);
- final animation = ref.watch(animationProvider);
- final dropDownAnimation = useAnimationController(
- duration: const Duration(milliseconds: 250),
- initialValue: 0.0,
- );
-
- void onBack(String path) {
- if (animation != null) {
- final controllerNotifier = ref.watch(
- swipeControllerProvider(animation).notifier,
- );
- controllerNotifier.toggle();
- }
- QR.to(path);
- pathForwardingNotifier.forward(path);
- hasScrolled.setHasScrolled(false);
- }
-
- return Column(
- children: [
- Container(height: 20),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Row(
- children: [
- Container(width: 25),
- GestureDetector(
- onTap: () {
- if (isAdmin) {
- if (dropDownAnimation.isDismissed) {
- dropDownAnimation.forward();
- } else {
- dropDownAnimation.reverse();
- }
- } else {
- onBack(SettingsRouter.root);
- }
- },
- behavior: HitTestBehavior.opaque,
- child: Row(
- children: [
- AsyncChild(
- value: profilePicture,
- builder: (context, file) => Row(
- children: [
- Stack(
- children: [
- Container(
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- boxShadow: [
- BoxShadow(
- color: Colors.black.withValues(
- alpha: 0.1,
- ),
- spreadRadius: 5,
- blurRadius: 10,
- offset: const Offset(0, 3),
- ),
- ],
- ),
- child: CircleAvatar(
- radius: 25,
- backgroundImage: file.isEmpty
- ? AssetImage(getTitanLogo())
- : Image.memory(file).image,
- ),
- ),
- if (isAdmin)
- Positioned(
- bottom: 0,
- right: 0,
- child: GestureDetector(
- onTap: () async {},
- child: Container(
- height: 18,
- width: 18,
- padding: const EdgeInsets.all(3),
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: const LinearGradient(
- colors: [
- ColorConstants.gradient1,
- ColorConstants.gradient2,
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: ColorConstants.gradient2
- .withValues(alpha: 0.3),
- spreadRadius: 1,
- blurRadius: 2,
- offset: const Offset(1, 2),
- ),
- ],
- ),
- child: const HeroIcon(
- HeroIcons.bolt,
- color: Colors.white,
- ),
- ),
- ),
- ),
- ],
- ),
- const SizedBox(width: 15),
- ],
- ),
- ),
- Column(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- SizedBox(
- width: 200,
- child: Text(
- user.nickname ?? user.firstname,
- style: TextStyle(
- color: Colors.grey.shade100,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- Container(height: 3),
- SizedBox(
- width: 200,
- child: Text(
- user.nickname != null
- ? "${user.firstname} ${user.name}"
- : user.name,
- style: TextStyle(
- color: Colors.grey.shade100,
- fontSize: 15,
- ),
- ),
- ),
- ],
- ),
- ],
- ),
- ),
- ],
- ),
- if (!isConnected)
- Container(
- margin: const EdgeInsets.only(right: 20),
- child: const HeroIcon(
- HeroIcons.signalSlash,
- color: Colors.white,
- size: 40,
- ),
- ),
- ],
- ),
- AnimatedBuilder(
- builder: (context, child) {
- return Opacity(
- opacity: dropDownAnimation.value,
- child: Padding(
- padding: const EdgeInsets.only(left: 25, top: 15),
- child: Column(
- children: [
- Transform.translate(
- offset: Offset(0, -10 * (1 - dropDownAnimation.value)),
- child: GestureDetector(
- behavior: HitTestBehavior.translucent,
- onTap: () => onBack(SettingsRouter.root),
- child: Row(
- children: [
- HeroIcon(
- HeroIcons.cog,
- color:
- pathForwarding.path.startsWith(
- SettingsRouter.root,
- )
- ? DrawerColorConstants.selectedText
- : DrawerColorConstants.lightText,
- size: 25,
- ),
- Container(width: 15),
- Text(
- DrawerTextConstants.settings,
- style: TextStyle(
- color:
- pathForwarding.path.startsWith(
- SettingsRouter.root,
- )
- ? DrawerColorConstants.selectedText
- : DrawerColorConstants.lightText,
- fontSize: 15,
- ),
- ),
- ],
- ),
- ),
- ),
- const SizedBox(height: 10),
- if (isAdmin)
- Transform.translate(
- offset: Offset(0, -15 * (1 - dropDownAnimation.value)),
- child: GestureDetector(
- behavior: HitTestBehavior.translucent,
- onTap: () => onBack(AdminRouter.root),
- child: Row(
- children: [
- HeroIcon(
- HeroIcons.userGroup,
- color:
- pathForwarding.path.startsWith(
- AdminRouter.root,
- )
- ? DrawerColorConstants.selectedText
- : DrawerColorConstants.lightText,
- size: 25,
- ),
- Container(width: 15),
- Text(
- DrawerTextConstants.admin,
- style: TextStyle(
- color:
- pathForwarding.path.startsWith(
- AdminRouter.root,
- )
- ? DrawerColorConstants.selectedText
- : DrawerColorConstants.lightText,
- fontSize: 15,
- ),
- ),
- Container(width: 25),
- ],
- ),
- ),
- ),
- ],
- ),
- ),
- );
- },
- animation: dropDownAnimation,
- ),
- ],
- );
- }
-}
diff --git a/lib/drawer/ui/email_change_popup.dart b/lib/drawer/ui/email_change_popup.dart
deleted file mode 100644
index 2344a06dbe..0000000000
--- a/lib/drawer/ui/email_change_popup.dart
+++ /dev/null
@@ -1,414 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/drawer/providers/already_displayed_popup.dart';
-import 'package:titan/loan/tools/constants.dart';
-import 'package:titan/tools/functions.dart';
-import 'package:titan/tools/providers/should_notify_provider.dart';
-import 'package:titan/tools/ui/builders/waiting_button.dart';
-import 'package:titan/user/providers/user_provider.dart';
-
-class Consts {
- Consts._();
-
- static const double padding = 20.0;
- static const double avatarRadius = 50.0;
- static const Color greenGradient1 = Color(0xff79a400);
- static const Color greenGradient2 = Color(0xff387200);
- static const Color redGradient1 = Color(0xFF9E131F);
- static const Color redGradient2 = Color(0xFF590512);
- static const String description =
- "L'administration a décidé de changer les adresses mails des étudiants.\nPour être sur de recevoir les mails en cas de perte du mot de passe, merci de renseigner la nouvelle (normalement elle est déjà préremplie 😉).";
- static const String descriptionMigration =
- "Vous avez créé un compte avec une adresse qui n'est pas une adresse centralienne.\nPour pouvoir accéder à cette application, vous devez changer cette adresse (normalement elle est déjà préremplie, on vous laisse vérifier et valider 😉).";
-}
-
-class EmailChangeDialog extends HookConsumerWidget {
- const EmailChangeDialog({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final user = ref.watch(userProvider);
- final shouldBeUser = ref.watch(shouldNotifyProvider);
- final userNotifier = ref.watch(asyncUserProvider.notifier);
- final alreadyDisplayedNotifier = ref.watch(
- alreadyDisplayedProvider.notifier,
- );
- final newEmail = shouldBeUser
- ? '${user.firstname.toLowerCase()}.${user.name.toLowerCase()}@etu.ec-lyon.fr'
- : '${user.email.split('@')[0]}@etu.ec-lyon.fr';
- final emailController = useTextEditingController(text: newEmail);
- final formKey = GlobalKey();
- final checkAnimationController = useAnimationController(
- duration: const Duration(milliseconds: 500),
- initialValue: 0,
- );
- final checkAnimation = CurvedAnimation(
- parent: checkAnimationController,
- curve: Curves.bounceOut,
- );
- final ValueNotifier currentState = useState(
- AsyncError("", StackTrace.current),
- );
- final displayForm = useState(true);
-
- useEffect(() {
- if (shouldBeUser) {
- emailController.text = newEmail;
- }
- return () {};
- }, [newEmail]);
-
- return GestureDetector(
- onTap: alreadyDisplayedNotifier.setAlreadyDisplayed,
- child: Container(
- color: Colors.black54,
- child: GestureDetector(
- onTap: () {},
- child: Dialog(
- shape: const RoundedRectangleBorder(
- borderRadius: BorderRadius.all(Radius.circular(20.0)),
- ),
- elevation: 0.0,
- insetPadding: const EdgeInsets.all(20.0),
- backgroundColor: Colors.transparent,
- child: Stack(
- clipBehavior: Clip.none,
- children: [
- Container(
- padding: const EdgeInsets.only(
- top: Consts.avatarRadius + Consts.padding,
- bottom: Consts.padding,
- left: Consts.padding,
- right: Consts.padding,
- ),
- margin: const EdgeInsets.only(top: Consts.avatarRadius),
- decoration: BoxDecoration(
- color: Colors.white,
- shape: BoxShape.rectangle,
- borderRadius: BorderRadius.circular(Consts.padding),
- boxShadow: const [
- BoxShadow(
- color: Colors.black26,
- blurRadius: 10.0,
- offset: Offset(0.0, 10.0),
- ),
- ],
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min, // To make the card compact
- children: [
- const Text(
- "Changer d'adresse mail",
- style: TextStyle(
- fontSize: 24.0,
- fontWeight: FontWeight.w700,
- ),
- ),
- const SizedBox(height: 12.0),
- displayForm.value
- ? Form(
- key: formKey,
- autovalidateMode: AutovalidateMode.always,
- child: Column(
- children: [
- Text(
- shouldBeUser
- ? Consts.descriptionMigration
- : Consts.description,
- textAlign: TextAlign.center,
- style: const TextStyle(fontSize: 16.0),
- ),
- const SizedBox(height: 15.0),
- TextFormField(
- controller: emailController,
- cursorColor: Colors.black,
- decoration: const InputDecoration(
- labelText: "Nouvelle adresse mail",
- floatingLabelStyle: TextStyle(
- color: Colors.black,
- fontWeight: FontWeight.bold,
- ),
- focusedBorder: UnderlineInputBorder(
- borderSide: BorderSide(
- color: Colors.black,
- width: 2.0,
- ),
- ),
- ),
- validator: (value) {
- if (value == null) {
- return LoanTextConstants.noValue;
- } else if (value.isEmpty) {
- return LoanTextConstants.noValue;
- } else if (!isStudent(value)) {
- return "Adresse mail invalide";
- }
- return null;
- },
- ),
- const SizedBox(height: 30.0),
- Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceBetween,
- children: [
- GestureDetector(
- onTap: alreadyDisplayedNotifier
- .setAlreadyDisplayed,
- child: Container(
- width: 100,
- padding: const EdgeInsets.symmetric(
- vertical: 16,
- ),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(
- 10,
- ),
- gradient: const LinearGradient(
- colors: [
- Colors.black87,
- Colors.black,
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: Colors.black.withValues(
- alpha: 0.3,
- ),
- blurRadius: 2.0,
- offset: const Offset(1.0, 2.0),
- ),
- ],
- ),
- child: const Center(
- child: Text(
- "Annuler",
- style: TextStyle(
- color: Colors.white,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ),
- ),
- WaitingButton(
- onTap: () async {
- if (formKey.currentState!
- .validate()) {
- currentState.value =
- const AsyncLoading();
- final result = await userNotifier
- .askMailMigration(
- emailController.text,
- );
- if (result) {
- currentState.value =
- const AsyncData("");
- checkAnimationController
- .forward();
- displayForm.value = false;
- } else {
- currentState.value = AsyncError(
- "Une erreur est survenue",
- StackTrace.current,
- );
- }
- }
- },
- waitingColor: Colors.black,
- builder: (child) => Container(
- width: 100,
- padding: const EdgeInsets.symmetric(
- vertical: 16,
- ),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(
- 10,
- ),
- color: Colors.grey.shade300,
- boxShadow: [
- BoxShadow(
- color: Colors.grey.withValues(
- alpha: 0.3,
- ),
- blurRadius: 2.0,
- offset: const Offset(1.0, 2.0),
- ),
- ],
- ),
- child: child,
- ),
- child: const Center(
- child: Text(
- "Confirmer",
- style: TextStyle(
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ),
- ],
- ),
- ],
- ),
- )
- : Expanded(
- child: Center(
- child: Column(
- children: [
- const Text(
- "Un mail de confirmation a été envoyé à l'adresse suivante, pour confirmer le changement :",
- textAlign: TextAlign.center,
- style: TextStyle(fontSize: 16.0),
- ),
- const SizedBox(height: 16.0),
- Text(
- emailController.text,
- textAlign: TextAlign.center,
- style: const TextStyle(
- fontSize: 16.0,
- fontWeight: FontWeight.bold,
- ),
- ),
- const Spacer(),
- GestureDetector(
- onTap: alreadyDisplayedNotifier
- .setAlreadyDisplayed,
- child: Container(
- width: 100,
- padding: const EdgeInsets.symmetric(
- vertical: 16,
- ),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(
- 10,
- ),
- color: Colors.grey.shade300,
- boxShadow: [
- BoxShadow(
- color: Colors.grey.withValues(
- alpha: 0.3,
- ),
- blurRadius: 2.0,
- offset: const Offset(1.0, 2.0),
- ),
- ],
- ),
- child: const Center(
- child: Text(
- "Fermer",
- style: TextStyle(
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- ],
- ),
- ),
- Positioned(
- left: Consts.padding,
- right: Consts.padding,
- child: currentState.value.when(
- data: (data) => AnimatedBuilder(
- animation: checkAnimationController,
- builder: (context, child) {
- return Container(
- width: Consts.avatarRadius * 2,
- height: Consts.avatarRadius * 2,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: const LinearGradient(
- colors: [
- Consts.greenGradient1,
- Consts.greenGradient2,
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: Consts.greenGradient2.withValues(
- alpha: 0.3,
- ),
- blurRadius: 10.0,
- offset: const Offset(0.0, 10.0),
- ),
- ],
- ),
- child: Center(
- child: Icon(
- Icons.check,
- color: Colors.white,
- size: 60 * checkAnimation.value,
- ),
- ),
- );
- },
- ),
- loading: () => Container(
- width: Consts.avatarRadius * 2,
- height: Consts.avatarRadius * 2,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: const LinearGradient(
- colors: [Consts.redGradient1, Consts.redGradient2],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: Consts.redGradient2.withValues(alpha: 0.3),
- blurRadius: 10.0,
- offset: const Offset(0.0, 10.0),
- ),
- ],
- ),
- child: const Center(
- child: CircularProgressIndicator(color: Colors.white),
- ),
- ),
- error: (error, stack) => Container(
- width: Consts.avatarRadius * 2,
- height: Consts.avatarRadius * 2,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- gradient: const LinearGradient(
- colors: [Consts.redGradient1, Consts.redGradient2],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- boxShadow: [
- BoxShadow(
- color: Consts.redGradient2.withValues(alpha: 0.3),
- blurRadius: 10.0,
- offset: const Offset(0.0, 10.0),
- ),
- ],
- ),
- child: const Center(
- child: HeroIcon(
- HeroIcons.exclamationCircle,
- size: 60,
- color: Colors.white,
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- );
- }
-}
diff --git a/lib/drawer/ui/fake_page.dart b/lib/drawer/ui/fake_page.dart
deleted file mode 100644
index ba07cf20f5..0000000000
--- a/lib/drawer/ui/fake_page.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:titan/drawer/tools/constants.dart';
-
-class FakePage extends StatelessWidget {
- const FakePage({super.key});
-
- @override
- Widget build(BuildContext context) {
- return Container(
- margin: const EdgeInsets.only(top: 10, bottom: 50),
- width: MediaQuery.of(context).size.width - 220,
- height: 420,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(30),
- boxShadow: const [
- BoxShadow(
- color: DrawerColorConstants.fakePageShadow,
- spreadRadius: 3,
- blurRadius: 5,
- offset: Offset(0, 3),
- ),
- ],
- color: DrawerColorConstants.fakePageBlue,
- ),
- );
- }
-}
diff --git a/lib/drawer/ui/list_module.dart b/lib/drawer/ui/list_module.dart
deleted file mode 100644
index b6f4009165..0000000000
--- a/lib/drawer/ui/list_module.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/drawer/providers/modules_provider.dart';
-import 'package:titan/drawer/ui/module.dart';
-
-class ListModule extends HookConsumerWidget {
- const ListModule({super.key});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final modules = ref.watch(listModuleProvider);
- final scrollController = useScrollController();
- return Scrollbar(
- controller: scrollController,
- interactive: true,
- radius: const Radius.circular(8),
- thumbVisibility: true,
- child: SingleChildScrollView(
- controller: scrollController,
- physics: const BouncingScrollPhysics(),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: modules.map((module) => ModuleUI(module: module)).toList(),
- ),
- ),
- );
- }
-}
diff --git a/lib/drawer/ui/module.dart b/lib/drawer/ui/module.dart
deleted file mode 100644
index a9b700850b..0000000000
--- a/lib/drawer/ui/module.dart
+++ /dev/null
@@ -1,78 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/drawer/class/module.dart';
-import 'package:titan/drawer/providers/animation_provider.dart';
-import 'package:titan/drawer/providers/swipe_provider.dart';
-import 'package:titan/drawer/tools/constants.dart';
-import 'package:titan/home/providers/scrolled_provider.dart';
-import 'package:titan/home/router.dart';
-import 'package:titan/tools/providers/path_forwarding_provider.dart';
-import 'package:qlevar_router/qlevar_router.dart';
-
-class ModuleUI extends HookConsumerWidget {
- const ModuleUI({super.key, required this.module});
-
- static Duration duration = const Duration(milliseconds: 200);
- final Module module;
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final pathForwardingNotifier = ref.read(pathForwardingProvider.notifier);
- final pathForwarding = ref.watch(pathForwardingProvider);
- final hasScrolled = ref.watch(hasScrolledProvider.notifier);
- final animation = ref.watch(animationProvider);
- return GestureDetector(
- behavior: HitTestBehavior.translucent,
- key: ValueKey(module.root),
- child: Container(
- margin: const EdgeInsets.only(top: 8, bottom: 8),
- width: 220,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Row(
- children: [
- Container(width: 25),
- Center(
- child: module.getIcon(
- module.root == pathForwarding.path
- ? DrawerColorConstants.selectedText
- : DrawerColorConstants.lightText,
- ),
- ),
- Container(width: 20),
- SizedBox(
- height: 50,
- child: Center(
- child: Text(
- module.name,
- style: TextStyle(
- color: module.root == pathForwarding.path
- ? DrawerColorConstants.selectedText
- : DrawerColorConstants.lightText,
- fontSize: 18,
- ),
- ),
- ),
- ),
- ],
- ),
- ],
- ),
- ),
- onTap: () {
- QR.to(module.root);
- pathForwardingNotifier.forward(module.root);
- if (animation != null) {
- final controllerNotifier = ref.watch(
- swipeControllerProvider(animation).notifier,
- );
- controllerNotifier.toggle();
- }
- if (pathForwarding.path != HomeRouter.root) {
- hasScrolled.setHasScrolled(false);
- }
- },
- );
- }
-}
diff --git a/lib/drawer/ui/notification_badge.dart b/lib/drawer/ui/notification_badge.dart
deleted file mode 100644
index ee6886cdab..0000000000
--- a/lib/drawer/ui/notification_badge.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-import 'package:badges/badges.dart' as badges;
-import 'package:flutter/material.dart';
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:titan/tools/providers/should_notify_provider.dart';
-
-class NotificationBadge extends HookConsumerWidget {
- final Widget child;
-
- const NotificationBadge({super.key, required this.child});
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final shouldNotify = ref.watch(shouldNotifyProvider);
- return badges.Badge(
- showBadge: shouldNotify,
- position: badges.BadgePosition.topStart(top: -5, start: -10),
- badgeStyle: badges.BadgeStyle(
- shape: badges.BadgeShape.circle,
- badgeGradient: badges.BadgeGradient.linear(
- colors: [Colors.red.shade600, Colors.red.shade800],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- elevation: 0,
- ),
- child: child,
- );
- }
-}
diff --git a/lib/event/providers/is_admin_provider.dart b/lib/event/providers/is_admin_provider.dart
index 78f1998e95..393cc60a2b 100644
--- a/lib/event/providers/is_admin_provider.dart
+++ b/lib/event/providers/is_admin_provider.dart
@@ -5,5 +5,5 @@ final isEventAdminProvider = StateProvider((ref) {
final me = ref.watch(userProvider);
return me.groups
.map((e) => e.id)
- .contains("53a669d6-84b1-4352-8d7c-421c1fbd9c6a");
+ .contains("b0357687-2211-410a-9e2a-144519eeaafa"); // admin_calendar
});
diff --git a/lib/event/router.dart b/lib/event/router.dart
index bd9d1656a0..fd655e1deb 100644
--- a/lib/event/router.dart
+++ b/lib/event/router.dart
@@ -1,7 +1,7 @@
-import 'package:either_dart/either.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:heroicons/heroicons.dart';
-import 'package:titan/drawer/class/module.dart';
+import 'package:titan/l10n/app_localizations.dart';
+import 'package:titan/navigation/class/module.dart';
import 'package:titan/event/providers/is_admin_provider.dart';
import 'package:titan/event/ui/pages/detail_page/detail_page.dart'
deferred as detail_page;
@@ -23,10 +23,10 @@ class EventRouter {
static const String addEdit = '/add_edit';
static const String detail = '/detail';
static final Module module = Module(
- name: "Évenements",
- icon: const Left(HeroIcons.calendar),
+ getName: (context) => AppLocalizations.of(context)!.moduleEvent,
+ getDescription: (context) =>
+ AppLocalizations.of(context)!.moduleEventDescription,
root: EventRouter.root,
- selected: false,
);
EventRouter(this.ref);
@@ -38,6 +38,10 @@ class EventRouter {
AuthenticatedMiddleware(ref),
DeferredLoadingMiddleware(main_page.loadLibrary),
],
+ pageType: QCustomPage(
+ transitionsBuilder: (_, animation, _, child) =>
+ FadeTransition(opacity: animation, child: child),
+ ),
children: [
QRoute(
path: admin,
diff --git a/lib/event/tools/constants.dart b/lib/event/tools/constants.dart
index cd62124273..9152f84640 100644
--- a/lib/event/tools/constants.dart
+++ b/lib/event/tools/constants.dart
@@ -1,79 +1,9 @@
-class EventTextConstants {
- static const String add = "Ajouter";
- static const String addEvent = "Ajouter un événement";
- static const String addedEvent = "Événement ajouté";
- static const String addingError = "Erreur lors de l'ajout";
- static const String allDay = "Toute la journée";
- static const String confirm = "Confirmer";
- static const String confirmEvent = "Confirmer l'événement ?";
- static const String confirmation = "Confirmation";
- static const String confirmed = "Confirmé";
- static const String dates = "Dates";
- static const String decline = "Refuser";
- static const String declineEvent = "Refuser l'événement ?";
- static const String declined = "Refusé";
- static const String delete = "Supprimer";
- static const String deletedEvent = "Événement supprimé";
- static const String deleting = "Suppression";
- static const String deletingError = "Erreur lors de la suppression";
- static const String deletingEvent = "Supprimer l'événement ?";
- static const String description = "Description";
- static const String edit = "Modifier";
- static const String editEvent = "Modifier un événement";
- static const String editedEvent = "Événement modifié";
- static const String editingError = "Erreur lors de la modification";
- static const String endDate = "Date de fin";
- static const String endHour = "Heure de fin";
- static const String error = "Erreur";
- static const String eventList = "Liste des événements";
- static const String eventType = "Type d'événement";
- static const String every = "Tous les";
- static const String history = "Historique";
- static const String incorrectOrMissingFields =
- "Certains champs sont incorrects ou manquants";
- static const String interval = "Intervalle";
- static const String invalidDates =
- "La date de fin doit être après la date de début";
- static const String invalidIntervalError =
- "Veuillez entrer un intervalle valide";
- static const String location = "Lieu";
- static const String myEvents = "Mes événements";
- static const String name = "Nom";
- static const String next = "Suivant";
- static const String no = "Non";
- static const String noCurrentEvent = "Aucun événement en cours";
- static const String noDateError = "Veuillez entrer une date";
- static const String noDaySelected = "Aucun jour sélectionné";
- static const String noDescriptionError = "Veuillez entrer une description";
- static const String noEvent = "Aucun événement";
- static const String noNameError = "Veuillez entrer un nom";
- static const String noOrganizerError = "Veuillez entrer un organisateur";
- static const String noPlaceError = "Veuillez entrer un lieu";
- static const String noPhoneRegistered = "Numéro non renseigné";
- static const String noRuleError = "Veuillez entrer une règle de récurrence";
- static const String organizer = "Organisateur";
- static const String other = "Autre";
- static const String pending = "En attente";
- static const String previous = "Précédent";
- static const String recurrence = "Récurrence";
- static const String recurrenceDays = "Jours de récurrence";
- static const String recurrenceEndDate = "Date de fin de la récurrence";
- static const String recurrenceRule = "Règle de récurrence";
- static const String room = "Salle";
- static const String startDate = "Date de début";
- static const String startHour = "Heure de début";
- static const String title = "Événements";
- static const String yes = "Oui";
- static const String eventEvery = "Toutes les";
- static const String weeks = "semaines";
-
- static const List dayList = [
- 'Lundi',
- 'Mardi',
- 'Mercredi',
- 'Jeudi',
- 'Vendredi',
- 'Samedi',
- 'Dimanche',
- ];
-}
+const eventDayKeys = [
+ 'eventDayMon',
+ 'eventDayTue',
+ 'eventDayWed',
+ 'eventDayThu',
+ 'eventDayFri',
+ 'eventDaySat',
+ 'eventDaySun',
+];
diff --git a/lib/event/tools/functions.dart b/lib/event/tools/functions.dart
index d74bfa5ff3..b0c1d65d2c 100644
--- a/lib/event/tools/functions.dart
+++ b/lib/event/tools/functions.dart
@@ -1,17 +1,18 @@
import 'dart:math';
+import 'package:flutter/material.dart';
import 'package:titan/event/class/event.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/tools/functions.dart';
+import 'package:titan/l10n/app_localizations.dart';
-String decisionToString(Decision d) {
+String decisionToString(Decision d, BuildContext context) {
switch (d) {
case Decision.approved:
- return EventTextConstants.confirmed;
+ return AppLocalizations.of(context)!.eventConfirmed;
case Decision.declined:
- return EventTextConstants.declined;
+ return AppLocalizations.of(context)!.eventDeclined;
case Decision.pending:
- return EventTextConstants.pending;
+ return AppLocalizations.of(context)!.eventPending;
}
}
@@ -80,3 +81,25 @@ String formatDelayToToday(DateTime date, DateTime now) {
}
return "Dans ${date.year - now.year} ans";
}
+
+String getLocalizedEventDay(BuildContext context, String key) {
+ final loc = AppLocalizations.of(context)!;
+ switch (key) {
+ case 'eventDayMon':
+ return loc.eventDayMon;
+ case 'eventDayTue':
+ return loc.eventDayTue;
+ case 'eventDayWed':
+ return loc.eventDayWed;
+ case 'eventDayThu':
+ return loc.eventDayThu;
+ case 'eventDayFri':
+ return loc.eventDayFri;
+ case 'eventDaySat':
+ return loc.eventDaySat;
+ case 'eventDaySun':
+ return loc.eventDaySun;
+ default:
+ return key;
+ }
+}
diff --git a/lib/event/ui/components/event_ui.dart b/lib/event/ui/components/event_ui.dart
index f5f0aafc92..3e57c99b7b 100644
--- a/lib/event/ui/components/event_ui.dart
+++ b/lib/event/ui/components/event_ui.dart
@@ -6,7 +6,6 @@ import 'package:titan/event/class/event.dart';
import 'package:titan/event/providers/event_provider.dart';
import 'package:titan/event/providers/user_event_list_provider.dart';
import 'package:titan/event/router.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/event/tools/functions.dart';
import 'package:titan/event/ui/components/edit_delete_button.dart';
import 'package:titan/tools/constants.dart';
@@ -15,6 +14,7 @@ import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/builders/waiting_button.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class EventUi extends ConsumerWidget {
final Event event;
@@ -34,6 +34,7 @@ class EventUi extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final now = DateTime.now();
final eventListNotifier = ref.watch(eventEventListProvider.notifier);
final eventNotifier = ref.watch(eventProvider.notifier);
@@ -127,6 +128,7 @@ class EventUi extends ConsumerWidget {
event.end,
event.recurrenceRule,
event.allDay,
+ locale.toString(),
),
style: TextStyle(
color: textColor.withValues(alpha: 0.7),
@@ -175,7 +177,7 @@ class EventUi extends ConsumerWidget {
Align(
alignment: Alignment.center,
child: Text(
- decisionToString(event.decision),
+ decisionToString(event.decision, context),
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: textColor,
@@ -209,7 +211,7 @@ class EventUi extends ConsumerWidget {
: Colors.grey.shade300,
child: Center(
child: Text(
- EventTextConstants.edit,
+ AppLocalizations.of(context)!.eventEdit,
style: TextStyle(
color: textColor,
fontSize: 15,
@@ -228,24 +230,35 @@ class EventUi extends ConsumerWidget {
context: context,
builder: (BuildContext context) {
return CustomDialogBox(
- descriptions:
- EventTextConstants.deletingEvent,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.eventDeletingEvent,
onYes: () async {
+ final deletedEventMsg =
+ AppLocalizations.of(
+ context,
+ )!.eventDeletedEvent;
+ final deletingErrorMsg =
+ AppLocalizations.of(
+ context,
+ )!.eventDeletingError;
final value = await eventListNotifier
.deleteEvent(event);
if (value) {
displayToastWithContext(
TypeMsg.msg,
- EventTextConstants.deletedEvent,
+ deletedEventMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- EventTextConstants.deletingError,
+ deletingErrorMsg,
);
}
},
- title: EventTextConstants.deleting,
+ title: AppLocalizations.of(
+ context,
+ )!.eventDeleting,
);
},
);
@@ -265,7 +278,7 @@ class EventUi extends ConsumerWidget {
),
child: Center(
child: Text(
- EventTextConstants.delete,
+ AppLocalizations.of(context)!.eventDelete,
style: TextStyle(
color: textColor,
fontSize: 15,
diff --git a/lib/event/ui/event.dart b/lib/event/ui/event.dart
index 7a1a7f962b..b8448f5ed8 100644
--- a/lib/event/ui/event.dart
+++ b/lib/event/ui/event.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:titan/event/router.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/tools/ui/widgets/top_bar.dart';
+import 'package:titan/tools/constants.dart';
class EventTemplate extends StatelessWidget {
final Widget child;
@@ -9,13 +9,18 @@ class EventTemplate extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return SafeArea(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- const TopBar(title: EventTextConstants.title, root: EventRouter.root),
- Expanded(child: child),
- ],
+ return Scaffold(
+ body: Container(
+ decoration: const BoxDecoration(color: ColorConstants.background),
+ child: SafeArea(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ const TopBar(root: EventRouter.root),
+ Expanded(child: child),
+ ],
+ ),
+ ),
),
);
}
diff --git a/lib/event/ui/pages/admin_page/admin_page.dart b/lib/event/ui/pages/admin_page/admin_page.dart
index e3d3e9df18..1f5682029e 100644
--- a/lib/event/ui/pages/admin_page/admin_page.dart
+++ b/lib/event/ui/pages/admin_page/admin_page.dart
@@ -3,12 +3,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/event/ui/event.dart';
import 'package:titan/event/class/event.dart';
import 'package:titan/event/providers/event_list_provider.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/event/ui/pages/admin_page/list_event.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/ui/layouts/refresher.dart';
import 'package:titan/tools/ui/widgets/calendar.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AdminPage extends HookConsumerWidget {
const AdminPage({super.key});
@@ -73,6 +73,7 @@ class AdminPage extends HookConsumerWidget {
}).toList();
return EventTemplate(
child: Refresher(
+ controller: ScrollController(),
onRefresh: () async {
await ref.watch(eventListProvider.notifier).loadEventList();
},
@@ -90,10 +91,10 @@ class AdminPage extends HookConsumerWidget {
if (pendingEvents.isEmpty &&
confirmedEvents.isEmpty &&
canceledEvents.isEmpty)
- const Center(
+ Center(
child: Text(
- EventTextConstants.noCurrentEvent,
- style: TextStyle(
+ AppLocalizations.of(context)!.eventNoCurrentEvent,
+ style: const TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold,
@@ -101,20 +102,20 @@ class AdminPage extends HookConsumerWidget {
),
),
ListEvent(
- title: EventTextConstants.pending,
+ title: AppLocalizations.of(context)!.eventPending,
events: pendingEvents,
canToggle: false,
),
ListEvent(
- title: EventTextConstants.confirmed,
+ title: AppLocalizations.of(context)!.eventConfirmed,
events: confirmedEvents,
),
ListEvent(
- title: EventTextConstants.declined,
+ title: AppLocalizations.of(context)!.eventDeclined,
events: canceledEvents,
),
ListEvent(
- title: EventTextConstants.history,
+ title: AppLocalizations.of(context)!.eventHistory,
events: confirmedEvents + canceledEvents + pendingEvents,
isHistory: true,
),
diff --git a/lib/event/ui/pages/admin_page/list_event.dart b/lib/event/ui/pages/admin_page/list_event.dart
index 3a5cdffd9b..7022e09598 100644
--- a/lib/event/ui/pages/admin_page/list_event.dart
+++ b/lib/event/ui/pages/admin_page/list_event.dart
@@ -7,7 +7,6 @@ import 'package:titan/event/providers/confirmed_event_list_provider.dart';
import 'package:titan/event/providers/event_list_provider.dart';
import 'package:titan/event/providers/event_provider.dart';
import 'package:titan/event/router.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/event/ui/components/event_ui.dart';
import 'package:titan/tools/functions.dart';
import 'package:titan/tools/token_expire_wrapper.dart';
@@ -15,6 +14,7 @@ import 'package:titan/tools/ui/widgets/align_left_text.dart';
import 'package:titan/tools/ui/widgets/custom_dialog_box.dart';
import 'package:titan/tools/ui/layouts/horizontal_list_view.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class ListEvent extends HookConsumerWidget {
final List events;
@@ -102,8 +102,10 @@ class ListEvent extends HookConsumerWidget {
context: context,
builder: (context) {
return CustomDialogBox(
- title: EventTextConstants.confirm,
- descriptions: EventTextConstants.confirmEvent,
+ title: AppLocalizations.of(context)!.eventConfirm,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.eventConfirmEvent,
onYes: () async {
await tokenExpireWrapper(ref, () async {
eventListNotifier
@@ -126,8 +128,10 @@ class ListEvent extends HookConsumerWidget {
context: context,
builder: (context) {
return CustomDialogBox(
- title: EventTextConstants.decline,
- descriptions: EventTextConstants.declineEvent,
+ title: AppLocalizations.of(context)!.eventDecline,
+ descriptions: AppLocalizations.of(
+ context,
+ )!.eventDeclineEvent,
onYes: () async {
await tokenExpireWrapper(ref, () async {
eventListNotifier
diff --git a/lib/event/ui/pages/detail_page/detail_page.dart b/lib/event/ui/pages/detail_page/detail_page.dart
index d49673500a..b7f0dc08f0 100644
--- a/lib/event/ui/pages/detail_page/detail_page.dart
+++ b/lib/event/ui/pages/detail_page/detail_page.dart
@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:heroicons/heroicons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:titan/event/providers/event_provider.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/event/ui/event.dart';
import 'package:titan/event/ui/components/event_ui.dart';
import 'package:titan/tools/functions.dart';
import 'package:url_launcher/url_launcher.dart';
+import 'package:titan/l10n/app_localizations.dart';
class DetailPage extends HookConsumerWidget {
final bool isAdmin;
@@ -97,7 +97,9 @@ class DetailPage extends HookConsumerWidget {
const SizedBox(height: 30),
Text(
event.applicant.phone ??
- EventTextConstants.noPhoneRegistered,
+ AppLocalizations.of(
+ context,
+ )!.eventNoPhoneRegistered,
style: const TextStyle(fontSize: 25),
),
const SizedBox(height: 50),
diff --git a/lib/event/ui/pages/event_pages/add_edit_event_page.dart b/lib/event/ui/pages/event_pages/add_edit_event_page.dart
index 27c8055eb3..ea66e974aa 100644
--- a/lib/event/ui/pages/event_pages/add_edit_event_page.dart
+++ b/lib/event/ui/pages/event_pages/add_edit_event_page.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:intl/intl.dart';
import 'package:titan/service/providers/room_list_provider.dart';
import 'package:titan/event/ui/event.dart';
import 'package:titan/event/ui/pages/event_pages/checkbox_entry.dart';
@@ -23,6 +24,7 @@ import 'package:titan/tools/ui/widgets/text_entry.dart';
import 'package:titan/user/providers/user_provider.dart';
import 'package:qlevar_router/qlevar_router.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
+import 'package:titan/l10n/app_localizations.dart';
class AddEditEventPage extends HookConsumerWidget {
final eventTypeScrollKey = GlobalKey();
@@ -31,6 +33,7 @@ class AddEditEventPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
+ final locale = Localizations.localeOf(context);
final now = DateTime.now();
final user = ref.watch(userProvider);
final event = ref.watch(eventProvider);
@@ -59,8 +62,8 @@ class AddEditEventPage extends HookConsumerWidget {
? ""
: processDateOnlyHour(event.start)
: allDay.value
- ? processDate(event.start)
- : processDateWithHour(event.start)
+ ? DateFormat.yMd(locale).format(event.start)
+ : DateFormat.yMd(locale).add_Hm().format(event.start)
: "",
);
final end = useTextEditingController(
@@ -70,8 +73,8 @@ class AddEditEventPage extends HookConsumerWidget {
? ""
: processDateOnlyHour(event.end)
: allDay.value
- ? processDate(event.end)
- : processDateWithHour(event.end)
+ ? DateFormat.yMd(locale).format(event.end)
+ : DateFormat.yMd(locale).add_Hm().format(event.end)
: "",
);
final interval = useTextEditingController(
@@ -81,7 +84,7 @@ class AddEditEventPage extends HookConsumerWidget {
);
final recurrenceEndDate = useTextEditingController(
text: event.recurrenceRule != ""
- ? processDate(
+ ? DateFormat.yMd(locale).format(
DateTime.parse(
event.recurrenceRule.split(";UNTIL=")[1].split(";")[0],
),
@@ -105,8 +108,8 @@ class AddEditEventPage extends HookConsumerWidget {
const SizedBox(height: 40),
AlignLeftText(
isEdit
- ? EventTextConstants.editEvent
- : EventTextConstants.addEvent,
+ ? AppLocalizations.of(context)!.eventEditEvent
+ : AppLocalizations.of(context)!.eventAddEvent,
padding: const EdgeInsets.symmetric(horizontal: 30),
color: Colors.grey,
),
@@ -141,16 +144,16 @@ class AddEditEventPage extends HookConsumerWidget {
children: [
TextEntry(
controller: name,
- label: EventTextConstants.name,
+ label: AppLocalizations.of(context)!.eventName,
),
const SizedBox(height: 30),
TextEntry(
controller: organizer,
- label: EventTextConstants.organizer,
+ label: AppLocalizations.of(context)!.eventOrganizer,
),
const SizedBox(height: 30),
CheckBoxEntry(
- title: EventTextConstants.recurrence,
+ title: AppLocalizations.of(context)!.eventRecurrence,
valueNotifier: recurrent,
onChanged: () {
start.text = "";
@@ -160,7 +163,7 @@ class AddEditEventPage extends HookConsumerWidget {
),
const SizedBox(height: 20),
CheckBoxEntry(
- title: EventTextConstants.allDay,
+ title: AppLocalizations.of(context)!.eventAllDay,
valueNotifier: allDay,
onChanged: () {
start.text = "";
@@ -174,21 +177,33 @@ class AddEditEventPage extends HookConsumerWidget {
children: [
Column(
children: [
- const Text(
- EventTextConstants.recurrenceDays,
- style: TextStyle(color: Colors.black),
+ Text(
+ AppLocalizations.of(
+ context,
+ )!.eventRecurrenceDays,
+ style: const TextStyle(
+ color: Colors.black,
+ ),
),
const SizedBox(height: 10),
Column(
- children: EventTextConstants.dayList
- .map(
- (e) => GestureDetector(
- onTap: () {
- selectedDaysNotifier.toggle(
- EventTextConstants.dayList
- .indexOf(e),
+ children: eventDayKeys
+ .asMap()
+ .entries
+ .map((entry) {
+ final index = entry.key;
+ final key = entry.value;
+ final localizedLabel =
+ getLocalizedEventDay(
+ context,
+ key,
);
- },
+
+ return GestureDetector(
+ onTap: () =>
+ selectedDaysNotifier.toggle(
+ index,
+ ),
behavior:
HitTestBehavior.opaque,
child: Row(
@@ -197,7 +212,7 @@ class AddEditEventPage extends HookConsumerWidget {
.spaceBetween,
children: [
Text(
- e,
+ localizedLabel,
style: TextStyle(
color: Colors
.grey
@@ -209,35 +224,38 @@ class AddEditEventPage extends HookConsumerWidget {
checkColor: Colors.white,
activeColor: Colors.black,
value:
- selectedDays[EventTextConstants
- .dayList
- .indexOf(e)],
- onChanged: (value) {
- selectedDaysNotifier
- .toggle(
- EventTextConstants
- .dayList
- .indexOf(e),
- );
- },
+ selectedDays[index],
+ onChanged: (_) =>
+ selectedDaysNotifier
+ .toggle(index),
),
],
),
- ),
- )
+ );
+ })
.toList(),
),
const SizedBox(height: 20),
- const Text(
- EventTextConstants.interval,
- style: TextStyle(color: Colors.black),
+ Text(
+ AppLocalizations.of(
+ context,
+ )!.eventInterval,
+ style: const TextStyle(
+ color: Colors.black,
+ ),
),
const SizedBox(height: 10),
TextEntry(
- label: EventTextConstants.interval,
+ label: AppLocalizations.of(
+ context,
+ )!.eventInterval,
controller: interval,
- prefix: EventTextConstants.eventEvery,
- suffix: EventTextConstants.weeks,
+ prefix: AppLocalizations.of(
+ context,
+ )!.eventEventEvery,
+ suffix: AppLocalizations.of(
+ context,
+ )!.eventWeeks,
isInt: true,
keyboardType: TextInputType.number,
),
@@ -251,15 +269,18 @@ class AddEditEventPage extends HookConsumerWidget {
start,
),
controller: start,
- label:
- EventTextConstants.startHour,
+ label: AppLocalizations.of(
+ context,
+ )!.eventStartHour,
),
const SizedBox(height: 30),
DateEntry(
onTap: () =>
getOnlyHourDate(context, end),
controller: end,
- label: EventTextConstants.endHour,
+ label: AppLocalizations.of(
+ context,
+ )!.eventEndHour,
),
const SizedBox(height: 30),
],
@@ -270,8 +291,9 @@ class AddEditEventPage extends HookConsumerWidget {
recurrenceEndDate,
),
controller: recurrenceEndDate,
- label: EventTextConstants
- .recurrenceEndDate,
+ label: AppLocalizations.of(
+ context,
+ )!.eventRecurrenceEndDate,
),
],
),
@@ -284,7 +306,9 @@ class AddEditEventPage extends HookConsumerWidget {
? getOnlyDayDate(context, start)
: getFullDate(context, start),
controller: start,
- label: EventTextConstants.startDate,
+ label: AppLocalizations.of(
+ context,
+ )!.eventStartDate,
),
const SizedBox(height: 30),
DateEntry(
@@ -292,7 +316,9 @@ class AddEditEventPage extends HookConsumerWidget {
? getOnlyDayDate(context, end)
: getFullDate(context, end),
controller: end,
- label: EventTextConstants.endDate,
+ label: AppLocalizations.of(
+ context,
+ )!.eventEndDate,
),
],
),
@@ -309,7 +335,7 @@ class AddEditEventPage extends HookConsumerWidget {
},
selected: isRoom.value,
child: Text(
- EventTextConstants.room,
+ AppLocalizations.of(context)!.eventRoom,
style: TextStyle(
color: isRoom.value ? Colors.white : Colors.black,
fontWeight: FontWeight.bold,
@@ -322,7 +348,7 @@ class AddEditEventPage extends HookConsumerWidget {
},
selected: !isRoom.value,
child: Text(
- EventTextConstants.other,
+ AppLocalizations.of(context)!.eventOther,
style: TextStyle(
color: isRoom.value ? Colors.black : Colors.white,
fontWeight: FontWeight.bold,
@@ -369,7 +395,7 @@ class AddEditEventPage extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 30),
child: TextEntry(
controller: location,
- label: EventTextConstants.location,
+ label: AppLocalizations.of(context)!.eventLocation,
),
),
const SizedBox(height: 30),
@@ -379,7 +405,7 @@ class AddEditEventPage extends HookConsumerWidget {
children: [
TextEntry(
controller: description,
- label: EventTextConstants.description,
+ label: AppLocalizations.of(context)!.eventDescription,
keyboardType: TextInputType.multiline,
),
const SizedBox(height: 50),
@@ -389,6 +415,18 @@ class AddEditEventPage extends HookConsumerWidget {
if (key.currentState == null) {
return;
}
+ final editedEventMsg = AppLocalizations.of(
+ context,
+ )!.eventEditedEvent;
+ final addedEventMsg = AppLocalizations.of(
+ context,
+ )!.eventAddedEvent;
+ final editingErrorMsg = AppLocalizations.of(
+ context,
+ )!.eventEditingError;
+ final addingErrorMsg = AppLocalizations.of(
+ context,
+ )!.eventAddingError;
if (key.currentState!.validate()) {
if (allDay.value) {
start.text =
@@ -398,13 +436,21 @@ class AddEditEventPage extends HookConsumerWidget {
}
if (end.text.contains("/") &&
isDateBefore(
- processDateBack(end.text),
- processDateBack(start.text),
+ processDateBack(
+ end.text,
+ locale.toString(),
+ ),
+ processDateBack(
+ start.text,
+ locale.toString(),
+ ),
)) {
displayToast(
context,
TypeMsg.error,
- EventTextConstants.invalidDates,
+ AppLocalizations.of(
+ context,
+ )!.eventInvalidDates,
);
} else if (recurrent.value &&
selectedDays
@@ -413,7 +459,9 @@ class AddEditEventPage extends HookConsumerWidget {
displayToast(
context,
TypeMsg.error,
- EventTextConstants.noDaySelected,
+ AppLocalizations.of(
+ context,
+ )!.eventNoDaySelected,
);
} else {
await tokenExpireWrapper(ref, () async {
@@ -421,12 +469,12 @@ class AddEditEventPage extends HookConsumerWidget {
String startString = start.text;
if (!startString.contains("/")) {
startString =
- "${processDate(now)} $startString";
+ "${DateFormat.yMd(locale).format(now)} $startString";
}
String endString = end.text;
if (!endString.contains("/")) {
endString =
- "${processDate(now)} $endString";
+ "${DateFormat.yMd(locale).format(now)} $endString";
}
if (recurrent.value) {
RecurrenceProperties recurrence =
@@ -436,7 +484,10 @@ class AddEditEventPage extends HookConsumerWidget {
recurrence.recurrenceRange =
RecurrenceRange.endDate;
recurrence.endDate = DateTime.parse(
- processDateBack(recurrenceEndDate.text),
+ processDateBack(
+ recurrenceEndDate.text,
+ locale.toString(),
+ ),
);
recurrence.weekDays = WeekDays.values
.where(
@@ -453,10 +504,16 @@ class AddEditEventPage extends HookConsumerWidget {
recurrenceRule = SfCalendar.generateRRule(
recurrence,
DateTime.parse(
- processDateBackWithHour(startString),
+ processDateBackWithHour(
+ startString,
+ locale.toString(),
+ ),
),
DateTime.parse(
- processDateBackWithHour(endString),
+ processDateBackWithHour(
+ endString,
+ locale.toString(),
+ ),
),
);
}
@@ -464,14 +521,20 @@ class AddEditEventPage extends HookConsumerWidget {
id: isEdit ? event.id : "",
description: description.text,
end: DateTime.parse(
- processDateBack(endString),
+ processDateBack(
+ endString,
+ locale.toString(),
+ ),
),
name: name.text,
organizer: organizer.text,
allDay: allDay.value,
location: location.text,
start: DateTime.parse(
- processDateBack(startString),
+ processDateBack(
+ startString,
+ locale.toString(),
+ ),
),
type: eventType.value,
recurrenceRule: recurrenceRule,
@@ -491,24 +554,24 @@ class AddEditEventPage extends HookConsumerWidget {
if (isEdit) {
displayToastWithContext(
TypeMsg.msg,
- EventTextConstants.editedEvent,
+ editedEventMsg,
);
} else {
displayToastWithContext(
TypeMsg.msg,
- EventTextConstants.addedEvent,
+ addedEventMsg,
);
}
} else {
if (isEdit) {
displayToastWithContext(
TypeMsg.error,
- EventTextConstants.editingError,
+ editingErrorMsg,
);
} else {
displayToastWithContext(
TypeMsg.error,
- EventTextConstants.addingError,
+ addingErrorMsg,
);
}
}
@@ -518,8 +581,8 @@ class AddEditEventPage extends HookConsumerWidget {
},
child: Text(
isEdit
- ? EventTextConstants.edit
- : EventTextConstants.add,
+ ? AppLocalizations.of(context)!.eventEdit
+ : AppLocalizations.of(context)!.eventAdd,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
diff --git a/lib/event/ui/pages/event_pages/checkbox_entry.dart b/lib/event/ui/pages/event_pages/checkbox_entry.dart
index 88d70206ca..118b51c6c1 100644
--- a/lib/event/ui/pages/event_pages/checkbox_entry.dart
+++ b/lib/event/ui/pages/event_pages/checkbox_entry.dart
@@ -30,7 +30,6 @@ class CheckBoxEntry extends StatelessWidget {
activeColor: Colors.black,
value: valueNotifier.value,
onChanged: (value) {
- valueNotifier.value = value!;
onChanged();
},
),
diff --git a/lib/event/ui/pages/main_page/main_page.dart b/lib/event/ui/pages/main_page/main_page.dart
index 384ef3df81..7b6a6898ad 100644
--- a/lib/event/ui/pages/main_page/main_page.dart
+++ b/lib/event/ui/pages/main_page/main_page.dart
@@ -6,7 +6,6 @@ import 'package:titan/event/providers/event_provider.dart';
import 'package:titan/event/providers/is_admin_provider.dart';
import 'package:titan/event/providers/user_event_list_provider.dart';
import 'package:titan/event/router.dart';
-import 'package:titan/event/tools/constants.dart';
import 'package:titan/event/ui/event.dart';
import 'package:titan/event/ui/components/event_ui.dart';
import 'package:titan/tools/ui/layouts/column_refresher.dart';
@@ -14,6 +13,7 @@ import 'package:titan/tools/ui/widgets/admin_button.dart';
import 'package:titan/tools/ui/builders/async_child.dart';
import 'package:titan/tools/ui/layouts/card_layout.dart';
import 'package:qlevar_router/qlevar_router.dart';
+import 'package:titan/l10n/app_localizations.dart';
class EventMainPage extends HookConsumerWidget {
const EventMainPage({super.key});
@@ -30,6 +30,7 @@ class EventMainPage extends HookConsumerWidget {
builder: (context, eventList) {
eventList.sort((a, b) => b.start.compareTo(a.start));
return ColumnRefresher(
+ controller: ScrollController(),
onRefresh: () async {
await eventListNotifier.loadConfirmedEvent();
},
@@ -44,8 +45,8 @@ class EventMainPage extends HookConsumerWidget {
children: [
Text(
eventList.isEmpty
- ? EventTextConstants.noEvent
- : EventTextConstants.myEvents,
+ ? AppLocalizations.of(context)!.eventNoEvent
+ : AppLocalizations.of(context)!.eventMyEvents,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
diff --git a/lib/feed/class/event.dart b/lib/feed/class/event.dart
new file mode 100644
index 0000000000..ce4a655f21
--- /dev/null
+++ b/lib/feed/class/event.dart
@@ -0,0 +1,112 @@
+import 'package:titan/tools/functions.dart';
+
+class Event {
+ late final String id;
+ late final String name;
+ late final DateTime start;
+ late final DateTime end;
+ late final bool allDay;
+ late final String location;
+ late final String recurrenceRule;
+ late final DateTime? ticketUrlOpening;
+ late final String associationId;
+ late final String? ticketUrl;
+ late final bool notification;
+
+ Event({
+ required this.id,
+ required this.name,
+ required this.start,
+ required this.end,
+ required this.allDay,
+ required this.location,
+ required this.recurrenceRule,
+ this.ticketUrlOpening,
+ required this.associationId,
+ this.ticketUrl,
+ required this.notification,
+ });
+
+ Event.fromJson(Map json) {
+ id = json['id'];
+ name = json['name'];
+ start = processDateFromAPI(json['start']);
+ end = processDateFromAPI(json['end']);
+ allDay = json['all_day'];
+ location = json['location'];
+ recurrenceRule = json['recurrence_rule'] ?? "";
+ ticketUrlOpening = json['ticket_url_opening'] != null
+ ? processDateFromAPI(json['ticket_url_opening'])
+ : null;
+ associationId = json['association_id'];
+ ticketUrl = json['ticket_url'];
+ notification = json['notification'] ?? true;
+ }
+
+ Map toJson() {
+ final Map data = {};
+
+ data['name'] = name;
+ data['start'] = processDateToAPI(start);
+ data['end'] = processDateToAPI(end);
+ data['all_day'] = allDay;
+ data['location'] = location;
+ data['recurrence_rule'] = recurrenceRule;
+ if (ticketUrlOpening != null) {
+ data['ticket_url_opening'] = processDateToAPI(ticketUrlOpening!);
+ }
+ data['association_id'] = associationId;
+ if (ticketUrl != null) {
+ data['ticket_url'] = ticketUrl;
+ }
+ data['notification'] = notification;
+ return data;
+ }
+
+ Event copyWith({
+ String? name,
+ DateTime? start,
+ DateTime? end,
+ String? location,
+ bool? allDay,
+ String? recurrenceRule,
+ DateTime? ticketUrlOpening,
+ String? associationId,
+ String? ticketUrl,
+ bool? hasRoom,
+ bool? notification,
+ }) {
+ return Event(
+ id: id,
+ name: name ?? this.name,
+ start: start ?? this.start,
+ end: end ?? this.end,
+ location: location ?? this.location,
+ recurrenceRule: recurrenceRule ?? this.recurrenceRule,
+ allDay: allDay ?? this.allDay,
+ ticketUrlOpening: ticketUrlOpening ?? this.ticketUrlOpening,
+ associationId: associationId ?? this.associationId,
+ ticketUrl: ticketUrl ?? this.ticketUrl,
+ notification: notification ?? this.notification,
+ );
+ }
+
+ Event.empty() {
+ id = '';
+ name = '';
+ start = DateTime.now();
+ end = DateTime.now();
+ allDay = false;
+ location = '';
+ recurrenceRule = '';
+ ticketUrlOpening = null;
+ associationId = '';
+ ticketUrl = null;
+ notification = true;
+ }
+
+ @override
+ String toString() {
+ return 'Event{name: $name, start: $start, end: $end, allDay: $allDay, location: $location, recurrenceRule: $recurrenceRule, ticketUrlOpening: $ticketUrlOpening, associationId: $associationId, ticketUrl: $ticketUrl, notification: $notification}';
+ }
+}
diff --git a/lib/feed/class/filter_state.dart b/lib/feed/class/filter_state.dart
new file mode 100644
index 0000000000..c430e711da
--- /dev/null
+++ b/lib/feed/class/filter_state.dart
@@ -0,0 +1,20 @@
+class FilterState {
+ final List selectedEntities;
+ final List selectedModules;
+
+ FilterState({required this.selectedEntities, required this.selectedModules});
+
+ FilterState copyWith({
+ List? selectedEntities,
+ List? selectedModules,
+ }) {
+ return FilterState(
+ selectedEntities: selectedEntities ?? this.selectedEntities,
+ selectedModules: selectedModules ?? this.selectedModules,
+ );
+ }
+
+ factory FilterState.empty() {
+ return FilterState(selectedEntities: [], selectedModules: []);
+ }
+}
diff --git a/lib/feed/class/news.dart b/lib/feed/class/news.dart
new file mode 100644
index 0000000000..a7d8176007
--- /dev/null
+++ b/lib/feed/class/news.dart
@@ -0,0 +1,102 @@
+import 'package:titan/feed/tools/function.dart';
+import 'package:titan/tools/functions.dart';
+
+class News {
+ final String id;
+ final String title;
+ final DateTime start;
+ final DateTime? end;
+ final String entity;
+ final String? location;
+ final DateTime? actionStart;
+ final String module;
+ final String moduleObjectId;
+ final NewsStatus status;
+
+ const News({
+ required this.id,
+ required this.title,
+ required this.start,
+ this.end,
+ required this.entity,
+ this.location,
+ this.actionStart,
+ required this.module,
+ required this.moduleObjectId,
+ required this.status,
+ });
+
+ News.fromJson(Map json)
+ : id = json['id'],
+ title = json['title'],
+ start = processDateFromAPI(json['start']),
+ end = json['end'] != null ? processDateFromAPI(json['end']) : null,
+ entity = json['entity'],
+ location = json['location'],
+ actionStart = json['action_start'] != null
+ ? processDateFromAPI(json['action_start'])
+ : null,
+ module = json['module'],
+ moduleObjectId = json['module_object_id'],
+ status = stringToNewsStatus(json['status']);
+
+ Map toJson() {
+ return {
+ 'id': id,
+ 'title': title,
+ 'start': processDateToAPI(start),
+ 'end': end != null ? processDateToAPI(end!) : null,
+ 'entity': entity,
+ 'location': location,
+ 'action_start': actionStart != null
+ ? processDateToAPI(actionStart!)
+ : null,
+ 'module': module,
+ 'module_object_id': moduleObjectId,
+ 'status': status.toString().split('.').last,
+ };
+ }
+
+ News copyWith({
+ String? id,
+ String? title,
+ DateTime? start,
+ DateTime? end,
+ String? entity,
+ String? location,
+ DateTime? actionStart,
+ String? module,
+ String? moduleObjectId,
+ NewsStatus? status,
+ }) {
+ return News(
+ id: id ?? this.id,
+ title: title ?? this.title,
+ start: start ?? this.start,
+ end: end ?? this.end,
+ entity: entity ?? this.entity,
+ location: location ?? this.location,
+ actionStart: actionStart ?? this.actionStart,
+ module: module ?? this.module,
+ moduleObjectId: moduleObjectId ?? this.moduleObjectId,
+ status: status ?? this.status,
+ );
+ }
+
+ @override
+ String toString() {
+ return 'News(id: $id, title: $title, start: $start, end: $end, entity: $entity, location: $location, actionStart: $actionStart, module: $module, moduleObjectId: $moduleObjectId, status: $status)';
+ }
+
+ News.empty()
+ : id = '',
+ title = '',
+ start = DateTime.now(),
+ end = null,
+ entity = '',
+ location = null,
+ actionStart = null,
+ module = '',
+ moduleObjectId = '',
+ status = NewsStatus.waitingApproval;
+}
diff --git a/lib/feed/class/ticket_url.dart b/lib/feed/class/ticket_url.dart
new file mode 100644
index 0000000000..f9c7842736
--- /dev/null
+++ b/lib/feed/class/ticket_url.dart
@@ -0,0 +1,27 @@
+class TicketUrl {
+ late final String ticketUrl;
+ TicketUrl({required this.ticketUrl});
+
+ TicketUrl.fromJson(Map json) {
+ ticketUrl = json['ticket_url'];
+ }
+
+ Map toJson() {
+ final Map data = {};
+ data['ticket_url'] = ticketUrl;
+ return data;
+ }
+
+ TicketUrl copyWith({String? ticketUrl}) {
+ return TicketUrl(ticketUrl: ticketUrl ?? this.ticketUrl);
+ }
+
+ TicketUrl.empty() {
+ ticketUrl = '';
+ }
+
+ @override
+ String toString() {
+ return 'TicketUrl{ticketUrl: $ticketUrl}';
+ }
+}
diff --git a/lib/feed/providers/admin_news_list_provider.dart b/lib/feed/providers/admin_news_list_provider.dart
new file mode 100644
index 0000000000..33561aa404
--- /dev/null
+++ b/lib/feed/providers/admin_news_list_provider.dart
@@ -0,0 +1,47 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/class/news.dart';
+import 'package:titan/feed/repositories/news_repository.dart';
+import 'package:titan/tools/providers/list_notifier.dart';
+
+class AdminNewsListNotifier extends ListNotifier {
+ final NewsRepository newsRepository;
+ AdminNewsListNotifier({required this.newsRepository})
+ : super(const AsyncValue.loading());
+
+ Future>> loadNewsList() async {
+ return await loadList(newsRepository.getAllNews);
+ }
+
+ Future addNews(News news) async {
+ return await add(newsRepository.createNews, news);
+ }
+
+ Future approveNews(News news) async {
+ return await update(
+ (news) => newsRepository.approveNews(news.id),
+ (newsList, news) =>
+ newsList..[newsList.indexWhere((d) => d.id == news.id)] = news,
+ news,
+ );
+ }
+
+ Future rejectNews(News news) async {
+ return await update(
+ (news) => newsRepository.rejectNews(news.id),
+ (newsList, news) =>
+ newsList..[newsList.indexWhere((d) => d.id == news.id)] = news,
+ news,
+ );
+ }
+}
+
+final adminNewsListProvider =
+ StateNotifierProvider>>((ref) {
+ final token = ref.watch(tokenProvider);
+ final newsRepository = NewsRepository()..setToken(token);
+ AdminNewsListNotifier newsListNotifier = AdminNewsListNotifier(
+ newsRepository: newsRepository,
+ )..loadNewsList();
+ return newsListNotifier;
+ });
diff --git a/lib/feed/providers/association_event_list_provider.dart b/lib/feed/providers/association_event_list_provider.dart
new file mode 100644
index 0000000000..cbaf277317
--- /dev/null
+++ b/lib/feed/providers/association_event_list_provider.dart
@@ -0,0 +1,49 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/class/event.dart';
+import 'package:titan/feed/repositories/event_repository.dart';
+import 'package:titan/tools/providers/list_notifier.dart';
+
+class AssociationEventsListNotifier extends ListNotifier {
+ final EventRepository eventsRepository;
+ AsyncValue> allNews = const AsyncValue.loading();
+ AssociationEventsListNotifier({required this.eventsRepository})
+ : super(const AsyncValue.loading());
+
+ Future>> loadAssociationEventList(
+ String associationId,
+ ) async {
+ return allNews = await loadList(
+ () => eventsRepository.getAssociationEventList(associationId),
+ );
+ }
+
+ Future updateEvent(Event event) async {
+ return await update(
+ (event) => eventsRepository.updateEvent(event),
+ (eventList, event) =>
+ eventList..[eventList.indexWhere((d) => d.id == event.id)] = event,
+ event,
+ );
+ }
+
+ Future deleteEvent(Event event) async {
+ return await update(
+ (event) => eventsRepository.deleteEvent(event.id),
+ (eventList, event) => eventList..removeWhere((d) => d.id == event.id),
+ event,
+ );
+ }
+}
+
+final associationEventsListProvider =
+ StateNotifierProvider<
+ AssociationEventsListNotifier,
+ AsyncValue>
+ >((ref) {
+ final token = ref.watch(tokenProvider);
+ final eventsRepository = EventRepository()..setToken(token);
+ AssociationEventsListNotifier newsListNotifier =
+ AssociationEventsListNotifier(eventsRepository: eventsRepository);
+ return newsListNotifier;
+ });
diff --git a/lib/feed/providers/event_image_provider.dart b/lib/feed/providers/event_image_provider.dart
new file mode 100644
index 0000000000..1c754c343e
--- /dev/null
+++ b/lib/feed/providers/event_image_provider.dart
@@ -0,0 +1,36 @@
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/repositories/event_image_repository.dart';
+import 'package:titan/tools/providers/single_notifier.dart';
+
+class EventImageNotifier extends SingleNotifier {
+ final eventImageRepository = EventImageRepository();
+ EventImageNotifier({required String token})
+ : super(const AsyncValue.loading()) {
+ eventImageRepository.setToken(token);
+ }
+
+ Future addEventImage(String id, Uint8List bytes) async {
+ final image = await eventImageRepository.addEventImage(bytes, id);
+ if (image.toString() != "") {
+ state = AsyncData(image);
+ return true;
+ }
+ return false;
+ }
+
+ void getEventImage(String id) async {
+ final image = await eventImageRepository.getEventImage(id);
+ state = AsyncData(image);
+ }
+}
+
+final eventImageProvider =
+ StateNotifierProvider>((ref) {
+ final token = ref.watch(tokenProvider);
+ return EventImageNotifier(token: token);
+ });
diff --git a/lib/feed/providers/event_provider.dart b/lib/feed/providers/event_provider.dart
new file mode 100644
index 0000000000..4cd01242da
--- /dev/null
+++ b/lib/feed/providers/event_provider.dart
@@ -0,0 +1,34 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/class/event.dart';
+import 'package:titan/feed/repositories/event_repository.dart';
+import 'package:titan/tools/providers/single_notifier.dart';
+
+class EventNotifier extends SingleNotifier {
+ final EventRepository eventRepository;
+ EventNotifier({required this.eventRepository})
+ : super(const AsyncValue.loading());
+
+ Future addEvent(Event event) async {
+ return await eventRepository.createEvent(event);
+ }
+
+ void fakeLoad() {
+ state = AsyncValue.data(Event.empty());
+ }
+
+ void setEvent(Event event) {
+ state = AsyncValue.data(event);
+ }
+}
+
+final eventProvider = StateNotifierProvider>((
+ ref,
+) {
+ final token = ref.watch(tokenProvider);
+ final eventRepository = EventRepository()..setToken(token);
+ EventNotifier eventListNotifier = EventNotifier(
+ eventRepository: eventRepository,
+ )..fakeLoad();
+ return eventListNotifier;
+});
diff --git a/lib/feed/providers/event_ticket_url_provider.dart b/lib/feed/providers/event_ticket_url_provider.dart
new file mode 100644
index 0000000000..1603e5e6a4
--- /dev/null
+++ b/lib/feed/providers/event_ticket_url_provider.dart
@@ -0,0 +1,23 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/feed/class/ticket_url.dart';
+import 'package:titan/feed/repositories/event_repository.dart';
+import 'package:titan/tools/providers/single_notifier.dart';
+
+class TicketUrlNotifier extends SingleNotifier {
+ final EventRepository eventRepository;
+ TicketUrlNotifier({required this.eventRepository})
+ : super(const AsyncValue.loading());
+
+ Future> getTicketUrl(String eventId) async {
+ return await load(() => eventRepository.getTicketUrl(eventId));
+ }
+}
+
+final ticketUrlProvider =
+ StateNotifierProvider>((ref) {
+ final eventRepository = ref.watch(eventRepositoryProvider);
+ TicketUrlNotifier notifier = TicketUrlNotifier(
+ eventRepository: eventRepository,
+ );
+ return notifier;
+ });
diff --git a/lib/feed/providers/filter_state_provider.dart b/lib/feed/providers/filter_state_provider.dart
new file mode 100644
index 0000000000..f0f479a701
--- /dev/null
+++ b/lib/feed/providers/filter_state_provider.dart
@@ -0,0 +1,15 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/feed/class/filter_state.dart';
+
+class FilterStateNotifier extends StateNotifier {
+ FilterStateNotifier() : super(FilterState.empty());
+
+ void setFilterState(FilterState i) {
+ state = i;
+ }
+}
+
+final filterStateProvider =
+ StateNotifierProvider((ref) {
+ return FilterStateNotifier();
+ });
diff --git a/lib/feed/providers/is_feed_admin_provider.dart b/lib/feed/providers/is_feed_admin_provider.dart
new file mode 100644
index 0000000000..b8d80b3f24
--- /dev/null
+++ b/lib/feed/providers/is_feed_admin_provider.dart
@@ -0,0 +1,9 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/user/providers/user_provider.dart';
+
+final isFeedAdminProvider = StateProvider((ref) {
+ final me = ref.watch(userProvider);
+ return me.groups
+ .map((e) => e.id)
+ .contains("59e3c4c2-e60f-44b6-b0d2-fa1b248423bb"); // admin_feed
+});
diff --git a/lib/feed/providers/is_user_a_member_of_an_association.dart b/lib/feed/providers/is_user_a_member_of_an_association.dart
new file mode 100644
index 0000000000..3d8523e3f1
--- /dev/null
+++ b/lib/feed/providers/is_user_a_member_of_an_association.dart
@@ -0,0 +1,10 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/admin/providers/my_association_list_provider.dart';
+
+final isUserAMemberOfAnAssociationProvider = Provider((ref) {
+ final myAssociation = ref.watch(asyncMyAssociationListProvider);
+ return myAssociation.maybeWhen(
+ data: (associations) => associations.isNotEmpty,
+ orElse: () => false,
+ );
+});
diff --git a/lib/feed/providers/news_image_provider.dart b/lib/feed/providers/news_image_provider.dart
new file mode 100644
index 0000000000..d3152546b4
--- /dev/null
+++ b/lib/feed/providers/news_image_provider.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/providers/news_images_provider.dart';
+import 'package:titan/feed/repositories/news_image_repository.dart';
+import 'package:titan/tools/providers/single_notifier.dart';
+
+class NewsImageNotifier extends SingleNotifier {
+ final newsImageRepository = NewsImageRepository();
+ final NewsImagesNotifier newsImagesNotifier;
+ NewsImageNotifier({required String token, required this.newsImagesNotifier})
+ : super(const AsyncValue.loading()) {
+ newsImageRepository.setToken(token);
+ }
+
+ Future getNewsImage(String id) async {
+ final image = await newsImageRepository.getNewsImage(id);
+ newsImagesNotifier.setTData(id, AsyncData([image]));
+ return image;
+ }
+}
+
+final newsImageProvider =
+ StateNotifierProvider>((ref) {
+ final token = ref.watch(tokenProvider);
+ final newsImagesNotifier = ref.watch(newsImagesProvider.notifier);
+ return NewsImageNotifier(
+ token: token,
+ newsImagesNotifier: newsImagesNotifier,
+ );
+ });
diff --git a/lib/feed/providers/news_images_provider.dart b/lib/feed/providers/news_images_provider.dart
new file mode 100644
index 0000000000..7db05cd5d7
--- /dev/null
+++ b/lib/feed/providers/news_images_provider.dart
@@ -0,0 +1,16 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/tools/providers/map_provider.dart';
+
+class NewsImagesNotifier extends MapNotifier {
+ NewsImagesNotifier() : super();
+}
+
+final newsImagesProvider =
+ StateNotifierProvider<
+ NewsImagesNotifier,
+ Map>?>
+ >((ref) {
+ NewsImagesNotifier advertPosterNotifier = NewsImagesNotifier();
+ return advertPosterNotifier;
+ });
diff --git a/lib/feed/providers/news_list_provider.dart b/lib/feed/providers/news_list_provider.dart
new file mode 100644
index 0000000000..8056cdbd32
--- /dev/null
+++ b/lib/feed/providers/news_list_provider.dart
@@ -0,0 +1,41 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/class/news.dart';
+import 'package:titan/feed/repositories/news_repository.dart';
+import 'package:titan/tools/providers/list_notifier.dart';
+
+class NewsListNotifier extends ListNotifier {
+ final NewsRepository newsRepository;
+ AsyncValue> allNews = const AsyncValue.loading();
+ NewsListNotifier({required this.newsRepository})
+ : super(const AsyncValue.loading());
+
+ Future>> loadNewsList() async {
+ return allNews = await loadList(newsRepository.getPublishedNews);
+ }
+
+ void filterNews(List entities, List modules) {
+ state = AsyncValue.data(
+ (allNews.value ?? []).where((news) {
+ final matchesEntity =
+ entities.isEmpty || entities.contains(news.entity);
+ final matchesModule = modules.isEmpty || modules.contains(news.module);
+ return matchesEntity && matchesModule;
+ }).toList(),
+ );
+ }
+
+ void resetFilters() {
+ state = AsyncValue.data(allNews.value ?? []);
+ }
+}
+
+final newsListProvider =
+ StateNotifierProvider>>((ref) {
+ final token = ref.watch(tokenProvider);
+ final newsRepository = NewsRepository()..setToken(token);
+ NewsListNotifier newsListNotifier = NewsListNotifier(
+ newsRepository: newsRepository,
+ )..loadNewsList();
+ return newsListNotifier;
+ });
diff --git a/lib/feed/repositories/event_image_repository.dart b/lib/feed/repositories/event_image_repository.dart
new file mode 100644
index 0000000000..0987c5c65a
--- /dev/null
+++ b/lib/feed/repositories/event_image_repository.dart
@@ -0,0 +1,27 @@
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/tools/repository/logo_repository.dart';
+
+class EventImageRepository extends LogoRepository {
+ @override
+ // ignore: overridden_fields
+ final ext = 'calendar/events/';
+
+ Future addEventImage(Uint8List bytes, String id) async {
+ final uint8List = await addLogo(bytes, id, suffix: "/image");
+ return Image.memory(uint8List);
+ }
+
+ Future getEventImage(String id) async {
+ final uint8List = await getLogo(id, suffix: "/image");
+ return Image.memory(uint8List);
+ }
+}
+
+final eventImageRepositoryProvider = Provider((ref) {
+ final token = ref.watch(tokenProvider);
+ return EventImageRepository()..setToken(token);
+});
diff --git a/lib/feed/repositories/event_repository.dart b/lib/feed/repositories/event_repository.dart
new file mode 100644
index 0000000000..0319ad0f2c
--- /dev/null
+++ b/lib/feed/repositories/event_repository.dart
@@ -0,0 +1,42 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:titan/auth/providers/openid_provider.dart';
+import 'package:titan/feed/class/event.dart';
+import 'package:titan/feed/class/ticket_url.dart';
+import 'package:titan/tools/repository/repository.dart';
+
+class EventRepository extends Repository {
+ @override
+ // ignore: overridden_fields
+ final ext = "calendar/events/";
+
+ Future createEvent(Event event) async {
+ return Event.fromJson(await create(event.toJson()));
+ }
+
+ Future> getEventList() async {
+ return List.from((await getList()).map((e) => Event.fromJson(e)));
+ }
+
+ Future> getAssociationEventList(String id) async {
+ return List.from(
+ (await getList(suffix: "associations/$id")).map((e) => Event.fromJson(e)),
+ );
+ }
+
+ Future getTicketUrl(String id) async {
+ return TicketUrl.fromJson(await getOne(id, suffix: "/ticket-url"));
+ }
+
+ Future