6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit f21d66a4
Changed files
azure/android-develop.yml | 2 +- comwell_key_app/android/app/build.gradle | 6 +- .../android/app/src/main/AndroidManifest.xml | 10 +- comwell_key_app/android/build.gradle | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- comwell_key_app/android/settings.gradle | 2 +- comwell_key_app/assets/images/maestro.svg | 7 + comwell_key_app/assets/images/master.png | Bin 802 -> 0 bytes comwell_key_app/assets/images/master.svg | 7 + comwell_key_app/assets/images/visa.svg | 5 + comwell_key_app/assets/translations/da-DK.json | 33 +- comwell_key_app/assets/translations/en-US.json | 29 +- comwell_key_app/ios/Flutter/AppFrameworkInfo.plist | 2 +- comwell_key_app/ios/Flutter/Debug.xcconfig | 2 +- comwell_key_app/ios/Flutter/Release.xcconfig | 2 +- comwell_key_app/ios/Podfile | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 185 +++-- comwell_key_app/ios/Runner/AppDelegate.swift | 44 +- .../ios/Runner/GoogleService-Info.plist | 12 +- comwell_key_app/ios/Runner/Info.plist | 36 +- comwell_key_app/ios/Runner/Runner.entitlements | 2 + .../ios/Runner/RunnerDebug-develop.entitlements | 2 + .../ios/Runner/RunnerRelease-Stage.entitlements | 4 + .../ios/Runner/RunnerRelease.entitlements | 8 + .../Runner/flavors/dev/GoogleService-Info.plist | 30 + .../Runner/flavors/prod/GoogleService-Info.plist | 30 + .../Runner/flavors/stage/GoogleService-Info.plist | 30 + .../Runner/flavors/test/GoogleService-Info.plist | 30 + .../ios/flavors/dev/GoogleService-Info.plist | 30 - .../ios/flavors/prod/GoogleService-Info.plist | 30 - .../ios/flavors/stage/GoogleService-Info.plist | 30 - .../ios/flavors/test/GoogleService-Info.plist | 30 - .../lib/.generated/database/comwell_db.g.dart | 91 ++- .../models/notification_permission.g.dart | 12 +- .../lib/.generated/overview/models/guest.g.dart | 17 - .../.generated/services/models/booking_dto.g.dart | 32 +- .../lib/.generated/services/models/user_dto.g.dart | 55 +- .../services/models/user_permissions.g.dart | 22 + .../authentication/authentication_repository.dart | 102 ++- .../booking_details/bloc/booking_details_bloc.dart | 59 +- .../bloc/booking_details_event.dart | 31 +- .../bloc/booking_details_state.dart | 2 + .../lib/booking_details/booking_details_page.dart | 51 +- .../booking_details_repository.dart | 55 +- .../components/booking_details_bottom_sheet.dart | 78 +- .../components/check_in_button.dart | 7 +- .../components/check_in_button_timer.dart | 6 +- .../components/check_out_button.dart | 2 +- .../components/get_keys_button.dart | 6 +- .../lib/booking_details/components/guest_list.dart | 83 +- .../components/preregister_button.dart | 17 +- .../components/remove_guest_dialog.dart | 93 +++ .../booking_details/components/share_button.dart | 51 +- .../components/unlock_room_button.dart | 6 +- .../lib/check_in/bloc/check_in_cubit.dart | 11 +- comwell_key_app/lib/check_in/check_in_page.dart | 28 +- .../lib/check_in/check_in_repository.dart | 34 +- .../lib/check_out/bloc/check_out_cubit.dart | 25 +- .../lib/check_out/bloc/check_out_state.dart | 12 +- comwell_key_app/lib/check_out/check_out_flow.dart | 4 +- .../check_out/components/accept_terms_toggle.dart | 7 +- .../check_out/components/apply_club_points.dart | 19 +- .../components/check_out_bottom_sheet.dart | 93 +-- .../lib/check_out/components/send_receipt.dart | 100 +++ .../lib/check_out/pages/checkout_payment_page.dart | 1 + .../choose_share_room_repository.dart | 2 + .../components/choose_room_widget.dart | 8 +- .../cubit/choose_share_room_cubit.dart | 1 + .../choose_share_room/pages/room_info_page.dart | 25 +- .../pages/share_room_base_page_template.dart | 171 ++++ .../choose_share_room/pages/share_room_page.dart | 161 +--- .../common/components/comwell_error_widget.dart | 14 +- .../lib/common/components/comwell_text_field.dart | 4 + .../shimmer_loader/share_room_shimmer_loader.dart | 177 ++++ .../template_pages/payment_page_template.dart | 13 +- comwell_key_app/lib/database/comwell_db.dart | 78 +- .../lib/database/daos/bookings_dao.dart | 15 +- .../lib/database/daos/notifications_dao.dart | 2 +- comwell_key_app/lib/database/daos/upsales_dao.dart | 5 +- .../lib/database/tables/notification_table.dart | 2 +- .../find_booking/confirmation_id_formatter.dart | 48 ++ .../lib/find_booking/cubit/find_booking_cubit.dart | 49 ++ .../lib/find_booking/cubit/find_booking_state.dart | 18 + .../lib/find_booking/find_booking_page.dart | 254 ++---- .../lib/find_booking/find_booking_repository.dart | 12 + comwell_key_app/lib/find_booking/loading_page.dart | 24 +- comwell_key_app/lib/key/key_page.dart | 30 +- comwell_key_app/lib/main.dart | 6 + .../lib/my_booking/cubit/my_booking_cubit.dart | 2 +- .../lib/my_booking/my_booking_page.dart | 83 +- .../my_booking/pages/my_booking_payment_page.dart | 1 + .../components/communications_list.dart | 21 +- .../notifications/cubit/notifications_cubit.dart | 24 +- .../models/notification_permission.dart | 55 +- .../lib/notifications/notifications_page.dart | 19 +- .../notifications/notifications_repository.dart | 17 +- .../overview/components/bill_download_modal.dart | 72 ++ .../components/booking_list_item_view.dart | 5 +- .../components/current_booking_list_item_view.dart | 117 ++- .../overview/components/guest_list_circles.dart | 60 ++ .../lib/overview/cubit/overview_cubit.dart | 44 +- comwell_key_app/lib/overview/models/booking.dart | 74 +- comwell_key_app/lib/overview/models/guest.dart | 19 +- comwell_key_app/lib/overview/models/room.dart | 13 + comwell_key_app/lib/overview/overview_page.dart | 49 +- .../past_cancelled_booking_detail_page.dart | 109 ++- .../overview/repository/overview_repository.dart | 80 +- .../lib/payment/cubit/payment_cubit.dart | 4 +- .../payment_cards/bloc/payment_cards_cubit.dart | 72 +- .../payment_cards/bloc/payment_cards_state.dart | 26 +- .../lib/payment_cards/payment_cards_page.dart | 115 ++- .../lib/pregistration/components/card_item.dart | 112 +-- .../components/clickable_card_item.dart | 31 - .../pregistration/components/information_card.dart | 2 +- .../pregistration/cubit/preregistration_cubit.dart | 136 +++- .../pregistration/cubit/preregistration_state.dart | 23 +- .../pregistration/pages/prereg_address_page.dart | 63 +- .../pages/prereg_confirmation_page.dart | 284 ++++--- .../pages/prereg_up_sales_catalog_page.dart | 28 +- .../pregistration/pregistration_repository.dart | 15 +- .../lib/pregistration/prereg_request_model.dart | 50 ++ .../lib/pregistration/preregistration_flow.dart | 149 ++-- comwell_key_app/lib/pregistration/utils/utils.dart | 8 +- .../profile/components/comwell_club_container.dart | 70 ++ .../profile/components/profile_page_widget.dart | 63 +- .../lib/profile/cubit/profile_cubit.dart | 3 +- .../lib/profile/profile_repository.dart | 69 +- .../components/intl_phone_field.dart | 5 +- .../cubit/profile_settings_cubit.dart | 4 +- .../lib/profile_settings/model/user.dart | 71 +- .../repostiory/profile_settings_repository.dart | 2 +- .../cubit/push_notification_cubit.dart | 88 ++ .../cubit/push_notification_state.dart | 19 + .../push_notification_repository.dart | 13 + .../push_notification_widget.dart | 17 + .../cubit/received_shared_booking_cubit.dart | 41 + .../cubit/received_shared_booking_state.dart | 26 + .../received_shared_booking_page.dart | 60 ++ .../cubit/received_shared_room_cubit.dart | 37 + .../cubit/received_shared_room_state.dart | 28 + .../received_shared_room_page.dart | 33 + comwell_key_app/lib/routing/app_router.dart | 904 +++++++++++---------- comwell_key_app/lib/routing/app_routes.dart | 3 + comwell_key_app/lib/services/api.dart | 199 +++-- comwell_key_app/lib/services/http_client.dart | 6 +- .../interceptors/response_handle_interceptor.dart | 173 ++-- .../lib/services/mappers/booking_mapper.dart | 69 +- .../lib/services/mappers/bookings_mapper.dart | 9 +- .../lib/services/mappers/user_mapper.dart | 79 +- .../lib/services/models/booking_dto.dart | 35 +- .../lib/services/models/simple_user_dto.dart | 43 + comwell_key_app/lib/services/models/user_dto.dart | 87 +- .../lib/services/models/user_permissions.dart | 31 + .../lib/services/utils/api_endpoints.dart | 15 +- .../lib/share/cubit/share_booking_cubit.dart | 35 +- .../lib/share/cubit/share_booking_state.dart | 9 +- comwell_key_app/lib/share/share_booking_page.dart | 169 +--- .../lib/share/share_booking_repository.dart | 41 + .../components/catalog/service_catalog.dart | 2 +- .../lib/up_sales/cubit/up_sales_cubit.dart | 4 +- .../lib/up_sales/up_sales_repository.dart | 2 +- comwell_key_app/lib/utils/firebase.dart | 8 + comwell_key_app/lib/utils/locator.dart | 6 + comwell_key_app/lib/utils/seos_repository.dart | 16 +- comwell_key_app/lib/utils/share_button_utils.dart | 2 +- .../templates/share_booking_base_template.dart | 197 +++++ comwell_key_app/pubspec.yaml | 15 +- comwell_key_app/scripts/run_dev.sh | 1 + .../authentication_repository.dart | 34 +- .../booking_details_bloc_test.dart | 6 +- .../booking_details_repository_test.dart | 4 +- comwell_key_app/test/key_test/key_bloc_test.dart | 9 +- .../test/overview_test/overview_cubic_test.dart | 61 +- .../overview_test/overview_repository_test.dart | 30 +- .../profile_settings_cubit_test.dart | 10 +- mobilekeys_sdk_plugin/android/build.gradle | 2 +- 176 files changed, 5218 insertions(+), 2708 deletions(-)
Diff
diff --git a/azure/android-develop.yml b/azure/android-develop.yml
index ce0806e8..f3fe1238 100644
--- a/azure/android-develop.yml
+++ b/azure/android-develop.yml
@@ -4,7 +4,7 @@ pr:
- develop
pool:
- vmImage: ubuntu-latest
+ vmImage: macos-15
variables:
- template: templates/env-vars.yml
diff --git a/comwell_key_app/android/app/build.gradle b/comwell_key_app/android/app/build.gradle
index 7287785b..16eba587 100644
--- a/comwell_key_app/android/app/build.gradle
+++ b/comwell_key_app/android/app/build.gradle
@@ -32,7 +32,7 @@ if (flutterVersionName == null) {
android {
compileSdkVersion 36
- ndkVersion "27.0.12077973" //flutter.ndkVersion
+ ndkVersion "28.2.13676358" //flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
@@ -83,8 +83,8 @@ android {
}
debug {
signingConfig signingConfigs.debug
- minifyEnabled true
- shrinkResources true
+ minifyEnabled false
+ shrinkResources false
}
}
flavorDimensions += "env"
diff --git a/comwell_key_app/android/app/src/main/AndroidManifest.xml b/comwell_key_app/android/app/src/main/AndroidManifest.xml
index d41bd863..7d737a7e 100644
--- a/comwell_key_app/android/app/src/main/AndroidManifest.xml
+++ b/comwell_key_app/android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
<uses-feature
android:name="android.hardware.nfc.hce"
android:required="false"/>
@@ -60,9 +66,7 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
-
-
-
+ <data android:scheme="comwell" />
</intent-filter>
</activity>
diff --git a/comwell_key_app/android/build.gradle b/comwell_key_app/android/build.gradle
index ac7f9c0f..8c56766b 100644
--- a/comwell_key_app/android/build.gradle
+++ b/comwell_key_app/android/build.gradle
@@ -1,12 +1,12 @@
buildscript {
- ext.kotlin_version = '1.8.22'
+ ext.kotlin_version = '2.1.0'
repositories {
google()
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.7.0'
+ classpath 'com.android.tools.build:gradle:8.13.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.android.tools:r8:8.1.72"
classpath 'com.google.gms:google-services:4.4.2'
diff --git a/comwell_key_app/android/gradle/wrapper/gradle-wrapper.properties b/comwell_key_app/android/gradle/wrapper/gradle-wrapper.properties
index 09523c0e..37f853b1 100644
--- a/comwell_key_app/android/gradle/wrapper/gradle-wrapper.properties
+++ b/comwell_key_app/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/comwell_key_app/android/settings.gradle b/comwell_key_app/android/settings.gradle
index d3d22523..9d5819a1 100644
--- a/comwell_key_app/android/settings.gradle
+++ b/comwell_key_app/android/settings.gradle
@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version '8.7.0' apply false
+ id "com.android.application" version '8.13.1' apply false
id "org.jetbrains.kotlin.android" version "2.2.0" apply false
}
diff --git a/comwell_key_app/assets/images/maestro.svg b/comwell_key_app/assets/images/maestro.svg
new file mode 100644
index 00000000..3cc553e6
--- /dev/null
+++ b/comwell_key_app/assets/images/maestro.svg
@@ -0,0 +1,7 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path d="M24.7236 6.75C28.743 6.75016 32.001 9.99603 32.001 14C32.001 18.004 28.743 21.2498 24.7236 21.25C22.9205 21.25 21.2714 20.5955 20 19.5137C18.7288 20.595 17.081 21.2499 15.2783 21.25C11.2588 21.25 8 18.0041 8 14C8 9.99594 11.2588 6.75 15.2783 6.75C17.0807 6.75007 18.7289 7.40426 20 8.48535C21.2713 7.4038 22.9207 6.75 24.7236 6.75Z" fill="#ED0006"/>
+<path d="M24.7236 6.75C28.7432 6.75 32.002 9.99594 32.002 14C32.002 18.0041 28.7432 21.25 24.7236 21.25C22.9207 21.25 21.2723 20.5953 20.001 19.5137C21.564 18.1839 22.5566 16.2081 22.5566 14C22.5566 11.7917 21.5643 9.81512 20.001 8.48535C21.2722 7.404 22.9209 6.75 24.7236 6.75Z" fill="#0099DF"/>
+<path d="M19.9922 8.48535C21.5557 9.81512 22.5479 11.7915 22.5479 14C22.5479 16.2083 21.5554 18.1839 19.9922 19.5137C18.4294 18.1839 17.4375 16.2079 17.4375 14C17.4375 11.7919 18.4291 9.81511 19.9922 8.48535Z" fill="#6C6BBD"/>
+</svg>
diff --git a/comwell_key_app/assets/images/master.png b/comwell_key_app/assets/images/master.png
deleted file mode 100644
index 2148a882..00000000
Binary files a/comwell_key_app/assets/images/master.png and /dev/null differ
diff --git a/comwell_key_app/assets/images/master.svg b/comwell_key_app/assets/images/master.svg
new file mode 100644
index 00000000..08656527
--- /dev/null
+++ b/comwell_key_app/assets/images/master.svg
@@ -0,0 +1,7 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path d="M24.7236 6.75C28.743 6.75016 32.001 9.99603 32.001 14C32.001 18.004 28.743 21.2498 24.7236 21.25C22.9205 21.25 21.2714 20.5955 20 19.5137C18.7288 20.595 17.081 21.2499 15.2783 21.25C11.2588 21.25 8 18.0041 8 14C8 9.99594 11.2588 6.75 15.2783 6.75C17.0807 6.75007 18.7289 7.40426 20 8.48535C21.2713 7.4038 22.9207 6.75 24.7236 6.75Z" fill="#ED0006"/>
+<path d="M24.7236 6.75C28.7432 6.75 32.002 9.99594 32.002 14C32.002 18.0041 28.7432 21.25 24.7236 21.25C22.9207 21.25 21.2723 20.5953 20.001 19.5137C21.564 18.1839 22.5566 16.2081 22.5566 14C22.5566 11.7917 21.5643 9.81512 20.001 8.48535C21.2722 7.404 22.9209 6.75 24.7236 6.75Z" fill="#F9A000"/>
+<path d="M19.9922 8.48535C21.5557 9.81512 22.5479 11.7915 22.5479 14C22.5479 16.2083 21.5554 18.1839 19.9922 19.5137C18.4294 18.1839 17.4375 16.2079 17.4375 14C17.4375 11.7919 18.4291 9.81511 19.9922 8.48535Z" fill="#FF5E00"/>
+</svg>
diff --git a/comwell_key_app/assets/images/visa.svg b/comwell_key_app/assets/images/visa.svg
new file mode 100644
index 00000000..2b852a9d
--- /dev/null
+++ b/comwell_key_app/assets/images/visa.svg
@@ -0,0 +1,5 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0003 18.43H9.57707L7.75995 11.448C7.6737 11.1268 7.49057 10.8429 7.22119 10.7091C6.54893 10.3728 5.80814 10.1051 5 9.97016V9.70136H8.90362C9.44238 9.70136 9.84645 10.1051 9.91379 10.5741L10.8566 15.6104L13.2787 9.70136H15.6345L12.0003 18.43ZM16.9799 18.43H14.6914L16.5759 9.70136H18.8644L16.9799 18.43ZM21.8256 12.1194C21.8929 11.6493 22.297 11.3805 22.7684 11.3805C23.5092 11.313 24.3161 11.448 24.9896 11.7831L25.3937 9.90379C24.7202 9.63498 23.9794 9.5 23.3072 9.5C21.086 9.5 19.4697 10.709 19.4697 12.387C19.4697 13.6636 20.6146 14.3338 21.4227 14.7376C22.297 15.1402 22.6337 15.4091 22.5664 15.8117C22.5664 16.4156 21.8929 16.6844 21.2207 16.6844C20.4125 16.6844 19.6044 16.4831 18.8648 16.1468L18.4607 18.0273C19.2688 18.3624 20.1431 18.4974 20.9513 18.4974C23.4418 18.5637 24.9896 17.3559 24.9896 15.5429C24.9896 13.2598 21.8256 13.126 21.8256 12.1194ZM32.9985 18.43L31.1814 9.70136H29.2296C28.8255 9.70136 28.4214 9.97016 28.2867 10.3728L24.9219 18.43H27.2778L27.748 17.1546H30.6426L30.912 18.43H32.9985ZM29.5677 12.052L30.2399 15.3416H28.3555L29.5677 12.052Z" fill="#172B85"/>
+</svg>
diff --git a/comwell_key_app/assets/translations/da-DK.json b/comwell_key_app/assets/translations/da-DK.json
index 7fec28e9..49d4d7bb 100644
--- a/comwell_key_app/assets/translations/da-DK.json
+++ b/comwell_key_app/assets/translations/da-DK.json
@@ -223,6 +223,8 @@
"digital_media_subtitle": "Ja, jeg vil gerne se personligt tilpassede digitale reklamer med nyheder, inspiration og invitationer til events. Jeg kan til enhver tid, afslutte min tilmelding.",
"sms": "SMS",
"sms_subtitle": "Ja, jeg vil gerne modtage SMS med nyheder, inspiration og invitationer til events. Jeg kan til enhver tid, afslutte min tilmelding.",
+ "email_subtitle": "Ja, jeg vil gerne modtage Emails med nyheder, inspiration og invitationer til events. Jeg kan til enhver tid, afslutte min tilmelding.",
+ "push_subtitle": "Ja, jeg vil gerne modtage push notifikationer med nyheder, inspiration og invitationer til events. Jeg kan til enhver tid, afslutte min tilmelding.",
"update_profile": "Opdater profil",
"profile_settings_invalid_date": "Ugyldigt dato",
"gender": "Køn",
@@ -316,6 +318,8 @@
"share_room_page_title": "Del værelse",
"share_room_page_subtitle": "Her kan du dele dit værelse med en anden gæst og give dem adgang til bookinginformation, nøglekort og Concierge",
"share_room_page_button": "Del dit værelse",
+ "received_shared_room_page_title": "Delt med dig",
+ "received_shared_room_page_subtitle": "{} har delt dette værelse med dig. Opholdet er blevet tilføjet til din konto.",
"addon": "Tilkøbt",
"added_to_room": "Tilkøbt på værelse",
"force_update_title": "Opdater for at bruge appen",
@@ -346,10 +350,35 @@
"overview_error_subtitle": "Der er sket en fejl. Vi kunne ikke loade dine bookinger. Prøv igen senere.",
"up_sales_error_title": "Tillægskøb er ikke tilgængelige i øjeblikket",
"up_sales_error_subtitle": "Der er sket en fejl. Prøv igen senere.",
-
"choose_room_error_title": "Kunne ikke vælge værelse",
"choose_room_error_subtitle": "Der er sket en fejl. Prøv igen senere eller gå til receptionen",
"share_booking_error_title": "Kunne ikke finde gæsterne på værelset",
"share_booking_error_subtitle": "Der er sket en fejl. Prøv igen senere.",
- "refresh_bookings": "Opdater bookinger"
+ "refresh_bookings": "Opdater bookinger",
+ "document_type": "Dokumenttype",
+ "document_type_passport": "Passport",
+ "document_type_id_card": "ID-kort",
+ "document_type_driver_license": "Kørekort",
+ "document_type_other": "Andet",
+ "document_number": "Dokumentnummer",
+ "up_sales_catalog_no_up_sales_title": "Ingen tilvalg tilgængelige",
+ "up_sales_catalog_no_up_sales_subtitle": "Der er ingen tilvalg tilgængelige i øjeblikket. Prøv igen senere.",
+ "payment_cards_missing_payment_method_title": "Manglende betalingsmetode",
+ "payment_cards_missing_payment_method_subtitle": "Vælg venligst en betalingsmetode for at fortsætte.",
+ "share_booking_page_error_title": "Fejl",
+ "share_booking_page_error_subtitle": "Denne booking er allerede delt.",
+ "share_booking_message": "{} vil gerne dele et ophold med dig på Comwell {}. Tilgå via linket: {}",
+ "received_shared_booking_page_subtitle": "{} har delt dette ophold med dig. Opholdet er blevet tilføjet til din konto.",
+ "check_in_page_error_title": "Værelse ikke fundet",
+ "check_in_page_error_subtitle": "Vi fandt ikke dit værelse. Du bedes henvende dig i receptionen.",
+ "remove_guest_overview": "Fjern",
+ "home_page": "Hjem",
+ "send_to_email": "Send til email",
+ "download_bill": "Få tilsendt kvittering",
+ "bill_email_placeholder": "E-mail",
+ "payment_overview_send_receipt_title": "Kvittering via email",
+ "payment_overview_send_receipt_subtitle": "VSendes til {}",
+ "payment_overview_send_comment_title": "Kommentar",
+ "payment_overview_send_receipt_hint": "Indsæt kommentar",
+ "key_page_room_prefix": "Værelse {}"
}
\ No newline at end of file
diff --git a/comwell_key_app/assets/translations/en-US.json b/comwell_key_app/assets/translations/en-US.json
index f0fe3de5..02c44339 100644
--- a/comwell_key_app/assets/translations/en-US.json
+++ b/comwell_key_app/assets/translations/en-US.json
@@ -226,6 +226,8 @@
"digital_media_subtitle": "Yes, I would like see personalized digital media advertising with news, inspiration and invitations to events. I can at any time, unsubscribe.",
"sms": "SMS",
"sms_subtitle": "Yes, I would like to recieve SMS with news, inspiration and invitations to events. I can at any time, unsubscribe.",
+ "email_subtitle": "Yes, I would like to receive emails with news, inspiration and invitations to events. I can at any time, unsubscribe.",
+ "push_subtitle": "Yes, I would like to receive push notifications with news, inspiration and invitations to events. I can at any time, unsubscribe.",
"update_profile": "Update profile",
"profile_settings_invalid_date": "Invalid date",
"gender": "Gender",
@@ -353,5 +355,30 @@
"choose_room_error_subtitle": "An error occurred. Please try again later or go to the reception.",
"share_booking_error_title": "Could not find guests on the room",
"share_booking_error_subtitle": "An error occurred. Please try again later.",
- "refresh_bookings": "Refresh bookings"
+ "refresh_bookings": "Refresh bookings",
+ "document_type": "Document type",
+ "document_type_passport": "Passport",
+ "document_type_id_card": "ID card",
+ "document_type_driver_license": "Driver license",
+ "document_type_other": "Other",
+ "document_number": "Document number",
+ "up_sales_catalog_no_up_sales_title": "No upgrades available",
+ "up_sales_catalog_no_up_sales_subtitle": "No upgrades are available at the moment. Please try again later.",
+ "payment_cards_missing_payment_method_title": "Missing payment method",
+ "payment_cards_missing_payment_method_subtitle": "Please select a payment method to continue.",
+ "share_booking_page_error_title": "Error",
+ "share_booking_page_error_subtitle": "This booking has already been shared.",
+ "share_booking_message": "{} would like to share a booking with you at Comwell {}. Access via the link: {}",
+ "check_in_page_error_title": "Room not found",
+ "check_in_page_error_subtitle": "We could not find the room. Please check in at the reception.",
+ "remove_guest_overview": "Remove",
+ "home_page": "Home",
+ "send_to_email": "Send to email",
+ "download_bill": "Get receipt",
+ "bill_email_placeholder": "E-mail",
+ "payment_overview_send_receipt_title": "Receipt via email",
+ "payment_overview_send_receipt_subtitle": "Sent to {}",
+ "payment_overview_send_comment_title": "Comment",
+ "payment_overview_send_receipt_hint": "Enter comment",
+ "key_page_room_prefix": "Room {}"
}
diff --git a/comwell_key_app/ios/Flutter/AppFrameworkInfo.plist b/comwell_key_app/ios/Flutter/AppFrameworkInfo.plist
index 1dc6cf76..163000d8 100644
--- a/comwell_key_app/ios/Flutter/AppFrameworkInfo.plist
+++ b/comwell_key_app/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
- <string>13.0</string>
+ <string>14.0</string>
</dict>
</plist>
diff --git a/comwell_key_app/ios/Flutter/Debug.xcconfig b/comwell_key_app/ios/Flutter/Debug.xcconfig
index 4cc7300a..72715b9d 100644
--- a/comwell_key_app/ios/Flutter/Debug.xcconfig
+++ b/comwell_key_app/ios/Flutter/Debug.xcconfig
@@ -1,3 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
-IPHONEOS_DEPLOYMENT_TARGET = 13.0
+IPHONEOS_DEPLOYMENT_TARGET = 14.0
diff --git a/comwell_key_app/ios/Flutter/Release.xcconfig b/comwell_key_app/ios/Flutter/Release.xcconfig
index 4c4670ac..b4b19fd6 100644
--- a/comwell_key_app/ios/Flutter/Release.xcconfig
+++ b/comwell_key_app/ios/Flutter/Release.xcconfig
@@ -1,3 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
-IPHONEOS_DEPLOYMENT_TARGET = 13.0
+IPHONEOS_DEPLOYMENT_TARGET = 14.0
diff --git a/comwell_key_app/ios/Podfile b/comwell_key_app/ios/Podfile
index 56ff852d..7e5ec7ed 100644
--- a/comwell_key_app/ios/Podfile
+++ b/comwell_key_app/ios/Podfile
@@ -43,7 +43,7 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
- # Set minimum iOS deployment target to 13.0 for all pods
+ # Set minimum iOS deployment target to 14.0 for all pods (required for Firebase Analytics)
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
end
diff --git a/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj b/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj
index 1523b4a8..b9904cbe 100644
--- a/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj
+++ b/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj
@@ -8,11 +8,11 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 2B49EF1CD36098C3404CFA1A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D7C4E628B4A8C8FA19DB8F9 /* Pods_Runner.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
60BC597D2DEF216C00031449 /* seos-mobile-keys-plugin in Frameworks */ = {isa = PBXBuildFile; productRef = 60BC597C2DEF216C00031449 /* seos-mobile-keys-plugin */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
- 95F88EFF99B226A52E9957A4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57B4780D9147B6DF08C495B2 /* Pods_Runner.framework */; };
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 */; };
@@ -20,22 +20,26 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
- 08C864E4F79559242B817D01 /* Pods-Runner.debug-test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-test.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-test.xcconfig"; sourceTree = "<group>"; };
+ 0BDE219EA1D3C22B7A31E9C6 /* 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 = "<group>"; };
+ 135648F9589BFC34ACA7DA53 /* Pods-Runner.release-stage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-stage.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-stage.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
- 318A697BE77A1E954F6664E4 /* Pods-Runner.release-stage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-stage.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-stage.xcconfig"; sourceTree = "<group>"; };
+ 2B9F5788CBD25701C7B1B78D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
- 4901D0E4F3B37EA5D3197A0B /* Pods-Runner.debug-debugtest.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-debugtest.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-debugtest.xcconfig"; sourceTree = "<group>"; };
- 57B4780D9147B6DF08C495B2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B69198D49EEB35ACD0FDDD8 /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig"; sourceTree = "<group>"; };
+ 5D7C4E628B4A8C8FA19DB8F9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5DF394EBD14723E019294B07 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
60E2CA932C11B89C00B7206F /* SeosMobileKeysSDK.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:333BNLD22V:ASSAABLOY AB"; lastKnownFileType = wrapper.xcframework; name = SeosMobileKeysSDK.xcframework; path = ../../mobilekeys_sdk_plugin/XCFrameworks/SeosMobileKeysSDK.xcframework; sourceTree = "<group>"; };
60F9AC682EA63667007B6C33 /* RunnerDebug-Develop.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "RunnerDebug-Develop.entitlements"; sourceTree = "<group>"; };
+ 705DF7EB5B3AB46731A85AED /* Pods-Runner.release-test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-test.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-test.xcconfig"; sourceTree = "<group>"; };
7230779B2D54E062000859B0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
7240E4472E65C12C0057933D /* RunnerRelease-Stage.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "RunnerRelease-Stage.entitlements"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 92B95ECB567D5196EBA3828F /* Pods-Runner.debug-test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-test.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-test.xcconfig"; sourceTree = "<group>"; };
+ 96EF2542F4AF151719D5EA21 /* Pods-Runner.release-develop.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-develop.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-develop.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -43,16 +47,10 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- 9DB5D744F222AF809D0EF6AC /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
- A04B4A7043BF9616A795D245 /* Pods-Runner.debug-stage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-stage.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-stage.xcconfig"; sourceTree = "<group>"; };
- A4335E7A08A94C23664353EF /* 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 = "<group>"; };
- A48ECB7F7A31D3161CE3A3E5 /* Pods-Runner.release-develop.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-develop.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-develop.xcconfig"; sourceTree = "<group>"; };
- ABE3404FC02B427934355159 /* 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 = "<group>"; };
- AD9174E615F0B41D5035C8F2 /* Pods-Runner.release-releasetest.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-releasetest.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-releasetest.xcconfig"; sourceTree = "<group>"; };
- E36AC966B46166C0AE035E3E /* Pods-Runner.release-test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-test.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-test.xcconfig"; sourceTree = "<group>"; };
- EFFE858D5EF8C407900AC1DC /* Pods-Runner.debug-develop.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-develop.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-develop.xcconfig"; sourceTree = "<group>"; };
+ 9E2CC9CA15E554B722C64803 /* 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 = "<group>"; };
+ B6148B461FD45FA45A72FA83 /* Pods-Runner.debug-stage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-stage.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-stage.xcconfig"; sourceTree = "<group>"; };
+ D4A038CEEFF3B0D07D30FB26 /* Pods-Runner.debug-develop.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-develop.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-develop.xcconfig"; sourceTree = "<group>"; };
FACB177B2A83B94A004F58BD /* RunnerRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerRelease.entitlements; sourceTree = "<group>"; };
- FAED4B1B969B8CE5315D9AC4 /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-prod.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -62,7 +60,7 @@
files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
60BC597D2DEF216C00031449 /* seos-mobile-keys-plugin in Frameworks */,
- 95F88EFF99B226A52E9957A4 /* Pods_Runner.framework in Frameworks */,
+ 2B49EF1CD36098C3404CFA1A /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -73,7 +71,7 @@
isa = PBXGroup;
children = (
60E2CA932C11B89C00B7206F /* SeosMobileKeysSDK.xcframework */,
- 57B4780D9147B6DF08C495B2 /* Pods_Runner.framework */,
+ 5D7C4E628B4A8C8FA19DB8F9 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -81,18 +79,16 @@
42AA14EF3F0DD359C7826166 /* Pods */ = {
isa = PBXGroup;
children = (
- A4335E7A08A94C23664353EF /* Pods-Runner.debug.xcconfig */,
- 4901D0E4F3B37EA5D3197A0B /* Pods-Runner.debug-debugtest.xcconfig */,
- EFFE858D5EF8C407900AC1DC /* Pods-Runner.debug-develop.xcconfig */,
- 9DB5D744F222AF809D0EF6AC /* Pods-Runner.release.xcconfig */,
- FAED4B1B969B8CE5315D9AC4 /* Pods-Runner.release-prod.xcconfig */,
- 318A697BE77A1E954F6664E4 /* Pods-Runner.release-stage.xcconfig */,
- AD9174E615F0B41D5035C8F2 /* Pods-Runner.release-releasetest.xcconfig */,
- ABE3404FC02B427934355159 /* Pods-Runner.profile.xcconfig */,
- A04B4A7043BF9616A795D245 /* Pods-Runner.debug-stage.xcconfig */,
- A48ECB7F7A31D3161CE3A3E5 /* Pods-Runner.release-develop.xcconfig */,
- 08C864E4F79559242B817D01 /* Pods-Runner.debug-test.xcconfig */,
- E36AC966B46166C0AE035E3E /* Pods-Runner.release-test.xcconfig */,
+ 0BDE219EA1D3C22B7A31E9C6 /* Pods-Runner.debug.xcconfig */,
+ B6148B461FD45FA45A72FA83 /* Pods-Runner.debug-stage.xcconfig */,
+ 92B95ECB567D5196EBA3828F /* Pods-Runner.debug-test.xcconfig */,
+ D4A038CEEFF3B0D07D30FB26 /* Pods-Runner.debug-develop.xcconfig */,
+ 2B9F5788CBD25701C7B1B78D /* Pods-Runner.release.xcconfig */,
+ 705DF7EB5B3AB46731A85AED /* Pods-Runner.release-test.xcconfig */,
+ 3B69198D49EEB35ACD0FDDD8 /* Pods-Runner.release-prod.xcconfig */,
+ 135648F9589BFC34ACA7DA53 /* Pods-Runner.release-stage.xcconfig */,
+ 96EF2542F4AF151719D5EA21 /* Pods-Runner.release-develop.xcconfig */,
+ 9E2CC9CA15E554B722C64803 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -155,7 +151,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 33EABF4A2D4A5E0856464406 /* [CP] Check Pods Manifest.lock */,
+ C127FFD0461F2A2AA31D57CE /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@@ -164,8 +160,8 @@
60A3A0652C6E297E0034E2D1 /* ShellScript */,
FA6E6FAC2B9ED1B9005DB4C1 /* Remove Duplicate Signature */,
860C1E40BA853F0F44EFE8BC /* FlutterFire: "flutterfire bundle-service-file" */,
- 00DF577D3CEBF547E7D96EF9 /* [CP] Embed Pods Frameworks */,
- EFC54F80FCA3407E35D9F2E0 /* [CP] Copy Pods Resources */,
+ 77CCE8EFD7D1A4984DC7BFF9 /* [CP] Embed Pods Frameworks */,
+ AA31E2AC231A6B04AB4EE9FC /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -232,45 +228,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 00DF577D3CEBF547E7D96EF9 /* [CP] Embed Pods Frameworks */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
- );
- name = "[CP] Embed Pods Frameworks";
- outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
- showEnvVarsInLog = 0;
- };
- 33EABF4A2D4A5E0856464406 /* [CP] Check Pods Manifest.lock */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
- );
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
- showEnvVarsInLog = 0;
- };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -304,6 +261,23 @@
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [ \"$XCODE_VERSION_MAJOR\" = \"1500\" ]; then\n echo \"Remove signature files (Xcode 15 workaround)\"\n\n rm -rf \"$BUILD_DIR/Release-TestRelease-iphoneos/seos_mobile_keys_plugin/SeosMobileKeysSDK.xcframework-ios.signature\"\n find \"$BUILD_DIR/${CONFIGURATION}-iphoneos\" -name \"*.signature\" -type f | xargs -r rm\nfi\n";
};
+ 77CCE8EFD7D1A4984DC7BFF9 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
860C1E40BA853F0F44EFE8BC /* FlutterFire: "flutterfire bundle-service-file" */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -337,7 +311,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
- EFC54F80FCA3407E35D9F2E0 /* [CP] Copy Pods Resources */ = {
+ AA31E2AC231A6B04AB4EE9FC /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -354,6 +328,28 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
+ C127FFD0461F2A2AA31D57CE /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
FA6E6FAC2B9ED1B9005DB4C1 /* Remove Duplicate Signature */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
@@ -453,7 +449,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = NO;
SDKROOT = iphoneos;
@@ -476,7 +472,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -533,7 +529,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
@@ -555,7 +551,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -616,7 +612,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
@@ -641,7 +637,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -697,7 +693,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
@@ -716,12 +712,14 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- CODE_SIGN_STYLE = Automatic;
- DEVELOPMENT_TEAM = 8RNV6AX4ZL;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8RNV6AX4ZL;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -729,6 +727,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.comwell.phoenix.dev;
PRODUCT_NAME = "Comwell Hotels";
PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "CP Dev";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -776,7 +775,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = NO;
ONLY_ACTIVE_ARCH = NO;
SDKROOT = iphoneos;
@@ -803,7 +802,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -860,7 +859,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
@@ -886,7 +885,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -949,7 +948,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -972,7 +971,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1029,7 +1028,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
@@ -1055,7 +1054,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1118,7 +1117,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -1167,7 +1166,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
@@ -1191,7 +1190,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1219,7 +1218,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Comwell Hotels";
- IPHONEOS_DEPLOYMENT_TARGET = 15.6;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/comwell_key_app/ios/Runner/AppDelegate.swift b/comwell_key_app/ios/Runner/AppDelegate.swift
index cff8eed2..1f15bb8d 100644
--- a/comwell_key_app/ios/Runner/AppDelegate.swift
+++ b/comwell_key_app/ios/Runner/AppDelegate.swift
@@ -1,9 +1,19 @@
import UIKit
import Flutter
-import Adyen
-import adyen_checkout
import MSAL
+#if canImport(adyen_checkout)
+ import adyen_checkout
+#endif
+
+#if canImport(AdyenActions)
+ import AdyenActions
+#endif
+
+#if canImport(AdyenDropIn)
+ import AdyenDropIn
+#endif
+
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
@@ -13,15 +23,23 @@ import MSAL
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
-
- override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
- // Handle Adyen redirect
- RedirectComponent.applicationDidOpen(from: url)
-
- // Handle MSAL response
- let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String
- let msalResult = MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApplication)
+
+ override func application(_ application: UIApplication,
+ continue userActivity: NSUserActivity,
+ restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
+ return super.application(application, continue: userActivity, restorationHandler: restorationHandler)
+}
+
+override func application(_ app: UIApplication,
+ open url: URL,
+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
+
+ RedirectComponent.applicationDidOpen(from: url)
- return msalResult
- }
-}
\ No newline at end of file
+ let sourceApplication = options[.sourceApplication] as? String
+ let msalResult = MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApplication)
+ let flutterResult = super.application(app, open: url, options: options)
+ return msalResult || flutterResult
+}
+
+}
diff --git a/comwell_key_app/ios/Runner/GoogleService-Info.plist b/comwell_key_app/ios/Runner/GoogleService-Info.plist
index e7e211f6..e54763e2 100644
--- a/comwell_key_app/ios/Runner/GoogleService-Info.plist
+++ b/comwell_key_app/ios/Runner/GoogleService-Info.plist
@@ -3,17 +3,17 @@
<plist version="1.0">
<dict>
<key>API_KEY</key>
- <string>AIzaSyBgjzugYkBvtPwrx8SHqHsCCPFwxuOquyQ</string>
+ <string>AIzaSyAApmLc6jj2GNlGrXVFtKJo3A5AD9EXqhA</string>
<key>GCM_SENDER_ID</key>
- <string>1031550326508</string>
+ <string>735004599167</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
- <string>com.comwell.phoenix</string>
+ <string>com.comwell.phoenix.dev</string>
<key>PROJECT_ID</key>
- <string>comwell-phoenix</string>
+ <string>comwell-phoenix-dev</string>
<key>STORAGE_BUCKET</key>
- <string>comwell-phoenix.firebasestorage.app</string>
+ <string>comwell-phoenix-dev.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
@@ -25,6 +25,6 @@
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
- <string>1:1031550326508:ios:05ac9b069efbbac9cdf7c9</string>
+ <string>1:735004599167:ios:276b6bc3fc059e9b82f04a</string>
</dict>
</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/Runner/Info.plist b/comwell_key_app/ios/Runner/Info.plist
index d21f52c2..064db7fc 100644
--- a/comwell_key_app/ios/Runner/Info.plist
+++ b/comwell_key_app/ios/Runner/Info.plist
@@ -30,13 +30,13 @@
<key>CFBundleURLTypes</key>
<array>
<dict>
- <key>CFBundleURLName</key>
- <string>MSAL</string>
+ <key>CFBundleURLName</key>
+ <string>MSAL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.com.comwell.phoenix.dev</string>
- <string>msauth.com.comwell.phoenix.stage</string>
- <string>msauth.com.comwell.phoenix</string>
+ <string>msauth.com.comwell.phoenix.stage</string>
+ <string>msauth.com.comwell.phoenix</string>
</array>
</dict>
<dict>
@@ -48,32 +48,38 @@
<array>
<string>seosmobileaccess</string>
</array>
-
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
+ <key>CFBundleURLName</key>
+ <string>checkoutshopper-test.adyen.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>app</string>
<string>https</string>
<string>adyencheckout</string>
</array>
+ </dict>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
<key>CFBundleURLName</key>
- <string>test.test</string>
- <key>CFBundleURLName</key>
- <string>checkout-test.adyen.com/v71/payments</string>
- <key>CFBundleURLName</key>
- <string>com.comwell.phoenix.test</string>
- <key>CFBundleURLName</key>
- <string>checkoutshopper-test.adyen.com</string>
-
+ <string>Comwell share booking</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>app</string>
+ <string>comwell</string>
+ </array>
</dict>
+ <dict/>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>FIREBASE_ANALYTICS_COLLECTION_ENABLED</key>
<false/>
+ <key>FlutterDeepLinkingEnabled</key>
+ <true/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauthv2</string>
@@ -99,6 +105,8 @@
<array>
<string>bluetooth-central</string>
<string>location</string>
+ <string>fetch</string>
+ <string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
@@ -122,4 +130,4 @@
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
-</plist>
\ No newline at end of file
+</plist>
diff --git a/comwell_key_app/ios/Runner/Runner.entitlements b/comwell_key_app/ios/Runner/Runner.entitlements
index fad49566..33fa91b8 100644
--- a/comwell_key_app/ios/Runner/Runner.entitlements
+++ b/comwell_key_app/ios/Runner/Runner.entitlements
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>aps-environment</key>
+ <string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>app://test.test</string>
diff --git a/comwell_key_app/ios/Runner/RunnerDebug-develop.entitlements b/comwell_key_app/ios/Runner/RunnerDebug-develop.entitlements
index fad49566..33fa91b8 100644
--- a/comwell_key_app/ios/Runner/RunnerDebug-develop.entitlements
+++ b/comwell_key_app/ios/Runner/RunnerDebug-develop.entitlements
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>aps-environment</key>
+ <string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>app://test.test</string>
diff --git a/comwell_key_app/ios/Runner/RunnerRelease-Stage.entitlements b/comwell_key_app/ios/Runner/RunnerRelease-Stage.entitlements
index 5d6e38ad..1cd9fe1f 100644
--- a/comwell_key_app/ios/Runner/RunnerRelease-Stage.entitlements
+++ b/comwell_key_app/ios/Runner/RunnerRelease-Stage.entitlements
@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>com.apple.developer.associated-domains</key>
+ <array/>
+ <key>aps-environment</key>
+ <string>development</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.microsoft.adalcache</string>
diff --git a/comwell_key_app/ios/Runner/RunnerRelease.entitlements b/comwell_key_app/ios/Runner/RunnerRelease.entitlements
index 5d97c0a3..53a12392 100644
--- a/comwell_key_app/ios/Runner/RunnerRelease.entitlements
+++ b/comwell_key_app/ios/Runner/RunnerRelease.entitlements
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>aps-environment</key>
+ <string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>app://test.test</string>
@@ -16,4 +18,10 @@
<string>$(AppIdentifierPrefix)com.microsoft.adalcache</string>
</array>
</dict>
+<dict>
+ <key>com.apple.developer.associated-domains</key>
+ <array>
+ <string>app://test.test</string>
+ </array>
+</dict>
</plist>
diff --git a/comwell_key_app/ios/Runner/flavors/dev/GoogleService-Info.plist b/comwell_key_app/ios/Runner/flavors/dev/GoogleService-Info.plist
new file mode 100644
index 00000000..e54763e2
--- /dev/null
+++ b/comwell_key_app/ios/Runner/flavors/dev/GoogleService-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>API_KEY</key>
+ <string>AIzaSyAApmLc6jj2GNlGrXVFtKJo3A5AD9EXqhA</string>
+ <key>GCM_SENDER_ID</key>
+ <string>735004599167</string>
+ <key>PLIST_VERSION</key>
+ <string>1</string>
+ <key>BUNDLE_ID</key>
+ <string>com.comwell.phoenix.dev</string>
+ <key>PROJECT_ID</key>
+ <string>comwell-phoenix-dev</string>
+ <key>STORAGE_BUCKET</key>
+ <string>comwell-phoenix-dev.firebasestorage.app</string>
+ <key>IS_ADS_ENABLED</key>
+ <false></false>
+ <key>IS_ANALYTICS_ENABLED</key>
+ <false></false>
+ <key>IS_APPINVITE_ENABLED</key>
+ <true></true>
+ <key>IS_GCM_ENABLED</key>
+ <true></true>
+ <key>IS_SIGNIN_ENABLED</key>
+ <true></true>
+ <key>GOOGLE_APP_ID</key>
+ <string>1:735004599167:ios:276b6bc3fc059e9b82f04a</string>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/Runner/flavors/prod/GoogleService-Info.plist b/comwell_key_app/ios/Runner/flavors/prod/GoogleService-Info.plist
new file mode 100644
index 00000000..e7e211f6
--- /dev/null
+++ b/comwell_key_app/ios/Runner/flavors/prod/GoogleService-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>API_KEY</key>
+ <string>AIzaSyBgjzugYkBvtPwrx8SHqHsCCPFwxuOquyQ</string>
+ <key>GCM_SENDER_ID</key>
+ <string>1031550326508</string>
+ <key>PLIST_VERSION</key>
+ <string>1</string>
+ <key>BUNDLE_ID</key>
+ <string>com.comwell.phoenix</string>
+ <key>PROJECT_ID</key>
+ <string>comwell-phoenix</string>
+ <key>STORAGE_BUCKET</key>
+ <string>comwell-phoenix.firebasestorage.app</string>
+ <key>IS_ADS_ENABLED</key>
+ <false></false>
+ <key>IS_ANALYTICS_ENABLED</key>
+ <false></false>
+ <key>IS_APPINVITE_ENABLED</key>
+ <true></true>
+ <key>IS_GCM_ENABLED</key>
+ <true></true>
+ <key>IS_SIGNIN_ENABLED</key>
+ <true></true>
+ <key>GOOGLE_APP_ID</key>
+ <string>1:1031550326508:ios:05ac9b069efbbac9cdf7c9</string>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/Runner/flavors/stage/GoogleService-Info.plist b/comwell_key_app/ios/Runner/flavors/stage/GoogleService-Info.plist
new file mode 100644
index 00000000..e49854eb
--- /dev/null
+++ b/comwell_key_app/ios/Runner/flavors/stage/GoogleService-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>API_KEY</key>
+ <string>AIzaSyCJT0sclbwev_-bMTlDafrWbnCw3y3jlhk</string>
+ <key>GCM_SENDER_ID</key>
+ <string>243753787895</string>
+ <key>PLIST_VERSION</key>
+ <string>1</string>
+ <key>BUNDLE_ID</key>
+ <string>com.comwell.phoenix.stage</string>
+ <key>PROJECT_ID</key>
+ <string>comwell-phoenix-stage</string>
+ <key>STORAGE_BUCKET</key>
+ <string>comwell-phoenix-stage.firebasestorage.app</string>
+ <key>IS_ADS_ENABLED</key>
+ <false></false>
+ <key>IS_ANALYTICS_ENABLED</key>
+ <false></false>
+ <key>IS_APPINVITE_ENABLED</key>
+ <true></true>
+ <key>IS_GCM_ENABLED</key>
+ <true></true>
+ <key>IS_SIGNIN_ENABLED</key>
+ <true></true>
+ <key>GOOGLE_APP_ID</key>
+ <string>1:243753787895:ios:24f02e213657204db98ef2</string>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/Runner/flavors/test/GoogleService-Info.plist b/comwell_key_app/ios/Runner/flavors/test/GoogleService-Info.plist
new file mode 100644
index 00000000..101a8a0a
--- /dev/null
+++ b/comwell_key_app/ios/Runner/flavors/test/GoogleService-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>API_KEY</key>
+ <string>AIzaSyBxrRSPwlP8zRk5afgGX4jiOu9l3M5F-60</string>
+ <key>GCM_SENDER_ID</key>
+ <string>454092419952</string>
+ <key>PLIST_VERSION</key>
+ <string>1</string>
+ <key>BUNDLE_ID</key>
+ <string>com.comwell.phoenix.test</string>
+ <key>PROJECT_ID</key>
+ <string>comwell-phoenix-test</string>
+ <key>STORAGE_BUCKET</key>
+ <string>comwell-phoenix-test.firebasestorage.app</string>
+ <key>IS_ADS_ENABLED</key>
+ <false></false>
+ <key>IS_ANALYTICS_ENABLED</key>
+ <false></false>
+ <key>IS_APPINVITE_ENABLED</key>
+ <true></true>
+ <key>IS_GCM_ENABLED</key>
+ <true></true>
+ <key>IS_SIGNIN_ENABLED</key>
+ <true></true>
+ <key>GOOGLE_APP_ID</key>
+ <string>1:454092419952:ios:4ebb640eb03646e8a995b5</string>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/flavors/dev/GoogleService-Info.plist b/comwell_key_app/ios/flavors/dev/GoogleService-Info.plist
deleted file mode 100644
index e54763e2..00000000
--- a/comwell_key_app/ios/flavors/dev/GoogleService-Info.plist
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>API_KEY</key>
- <string>AIzaSyAApmLc6jj2GNlGrXVFtKJo3A5AD9EXqhA</string>
- <key>GCM_SENDER_ID</key>
- <string>735004599167</string>
- <key>PLIST_VERSION</key>
- <string>1</string>
- <key>BUNDLE_ID</key>
- <string>com.comwell.phoenix.dev</string>
- <key>PROJECT_ID</key>
- <string>comwell-phoenix-dev</string>
- <key>STORAGE_BUCKET</key>
- <string>comwell-phoenix-dev.firebasestorage.app</string>
- <key>IS_ADS_ENABLED</key>
- <false></false>
- <key>IS_ANALYTICS_ENABLED</key>
- <false></false>
- <key>IS_APPINVITE_ENABLED</key>
- <true></true>
- <key>IS_GCM_ENABLED</key>
- <true></true>
- <key>IS_SIGNIN_ENABLED</key>
- <true></true>
- <key>GOOGLE_APP_ID</key>
- <string>1:735004599167:ios:276b6bc3fc059e9b82f04a</string>
-</dict>
-</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/flavors/prod/GoogleService-Info.plist b/comwell_key_app/ios/flavors/prod/GoogleService-Info.plist
deleted file mode 100644
index e7e211f6..00000000
--- a/comwell_key_app/ios/flavors/prod/GoogleService-Info.plist
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>API_KEY</key>
- <string>AIzaSyBgjzugYkBvtPwrx8SHqHsCCPFwxuOquyQ</string>
- <key>GCM_SENDER_ID</key>
- <string>1031550326508</string>
- <key>PLIST_VERSION</key>
- <string>1</string>
- <key>BUNDLE_ID</key>
- <string>com.comwell.phoenix</string>
- <key>PROJECT_ID</key>
- <string>comwell-phoenix</string>
- <key>STORAGE_BUCKET</key>
- <string>comwell-phoenix.firebasestorage.app</string>
- <key>IS_ADS_ENABLED</key>
- <false></false>
- <key>IS_ANALYTICS_ENABLED</key>
- <false></false>
- <key>IS_APPINVITE_ENABLED</key>
- <true></true>
- <key>IS_GCM_ENABLED</key>
- <true></true>
- <key>IS_SIGNIN_ENABLED</key>
- <true></true>
- <key>GOOGLE_APP_ID</key>
- <string>1:1031550326508:ios:05ac9b069efbbac9cdf7c9</string>
-</dict>
-</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/flavors/stage/GoogleService-Info.plist b/comwell_key_app/ios/flavors/stage/GoogleService-Info.plist
deleted file mode 100644
index e49854eb..00000000
--- a/comwell_key_app/ios/flavors/stage/GoogleService-Info.plist
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>API_KEY</key>
- <string>AIzaSyCJT0sclbwev_-bMTlDafrWbnCw3y3jlhk</string>
- <key>GCM_SENDER_ID</key>
- <string>243753787895</string>
- <key>PLIST_VERSION</key>
- <string>1</string>
- <key>BUNDLE_ID</key>
- <string>com.comwell.phoenix.stage</string>
- <key>PROJECT_ID</key>
- <string>comwell-phoenix-stage</string>
- <key>STORAGE_BUCKET</key>
- <string>comwell-phoenix-stage.firebasestorage.app</string>
- <key>IS_ADS_ENABLED</key>
- <false></false>
- <key>IS_ANALYTICS_ENABLED</key>
- <false></false>
- <key>IS_APPINVITE_ENABLED</key>
- <true></true>
- <key>IS_GCM_ENABLED</key>
- <true></true>
- <key>IS_SIGNIN_ENABLED</key>
- <true></true>
- <key>GOOGLE_APP_ID</key>
- <string>1:243753787895:ios:24f02e213657204db98ef2</string>
-</dict>
-</plist>
\ No newline at end of file
diff --git a/comwell_key_app/ios/flavors/test/GoogleService-Info.plist b/comwell_key_app/ios/flavors/test/GoogleService-Info.plist
deleted file mode 100644
index 101a8a0a..00000000
--- a/comwell_key_app/ios/flavors/test/GoogleService-Info.plist
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>API_KEY</key>
- <string>AIzaSyBxrRSPwlP8zRk5afgGX4jiOu9l3M5F-60</string>
- <key>GCM_SENDER_ID</key>
- <string>454092419952</string>
- <key>PLIST_VERSION</key>
- <string>1</string>
- <key>BUNDLE_ID</key>
- <string>com.comwell.phoenix.test</string>
- <key>PROJECT_ID</key>
- <string>comwell-phoenix-test</string>
- <key>STORAGE_BUCKET</key>
- <string>comwell-phoenix-test.firebasestorage.app</string>
- <key>IS_ADS_ENABLED</key>
- <false></false>
- <key>IS_ANALYTICS_ENABLED</key>
- <false></false>
- <key>IS_APPINVITE_ENABLED</key>
- <true></true>
- <key>IS_GCM_ENABLED</key>
- <true></true>
- <key>IS_SIGNIN_ENABLED</key>
- <true></true>
- <key>GOOGLE_APP_ID</key>
- <string>1:454092419952:ios:4ebb640eb03646e8a995b5</string>
-</dict>
-</plist>
\ No newline at end of file
diff --git a/comwell_key_app/lib/.generated/database/comwell_db.g.dart b/comwell_key_app/lib/.generated/database/comwell_db.g.dart
index 46da1ebe..e63ec01a 100644
--- a/comwell_key_app/lib/.generated/database/comwell_db.g.dart
+++ b/comwell_key_app/lib/.generated/database/comwell_db.g.dart
@@ -389,11 +389,11 @@ class $NotificationPermissionEntityTable extends NotificationPermissionEntity
final GeneratedDatabase attachedDatabase;
final String? _alias;
$NotificationPermissionEntityTable(this.attachedDatabase, [this._alias]);
- static const VerificationMeta _idMeta = const VerificationMeta('id');
+ static const VerificationMeta _codeMeta = const VerificationMeta('code');
@override
- late final GeneratedColumn<int> id = GeneratedColumn<int>(
- 'id', aliasedName, false,
- type: DriftSqlType.int,
+ late final GeneratedColumn<String> code = GeneratedColumn<String>(
+ 'code', aliasedName, false,
+ type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'));
static const VerificationMeta _jsonMeta = const VerificationMeta('json');
@@ -402,7 +402,7 @@ class $NotificationPermissionEntityTable extends NotificationPermissionEntity
'json', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
@override
- List<GeneratedColumn> get $columns => [id, json];
+ List<GeneratedColumn> get $columns => [code, json];
@override
String get aliasedName => _alias ?? actualTableName;
@override
@@ -414,10 +414,11 @@ class $NotificationPermissionEntityTable extends NotificationPermissionEntity
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
- if (data.containsKey('id')) {
- context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+ if (data.containsKey('code')) {
+ context.handle(
+ _codeMeta, code.isAcceptableOrUnknown(data['code']!, _codeMeta));
} else if (isInserting) {
- context.missing(_idMeta);
+ context.missing(_codeMeta);
}
if (data.containsKey('json')) {
context.handle(
@@ -435,8 +436,8 @@ class $NotificationPermissionEntityTable extends NotificationPermissionEntity
{String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return NotificationPermissionDb(
- id: attachedDatabase.typeMapping
- .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+ code: attachedDatabase.typeMapping
+ .read(DriftSqlType.string, data['${effectivePrefix}code'])!,
json: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}json'])!,
);
@@ -450,20 +451,20 @@ class $NotificationPermissionEntityTable extends NotificationPermissionEntity
class NotificationPermissionDb extends DataClass
implements Insertable<NotificationPermissionDb> {
- final int id;
+ final String code;
final String json;
- const NotificationPermissionDb({required this.id, required this.json});
+ const NotificationPermissionDb({required this.code, required this.json});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
- map['id'] = Variable<int>(id);
+ map['code'] = Variable<String>(code);
map['json'] = Variable<String>(json);
return map;
}
NotificationPermissionEntityCompanion toCompanion(bool nullToAbsent) {
return NotificationPermissionEntityCompanion(
- id: Value(id),
+ code: Value(code),
json: Value(json),
);
}
@@ -472,7 +473,7 @@ class NotificationPermissionDb extends DataClass
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return NotificationPermissionDb(
- id: serializer.fromJson<int>(json['id']),
+ code: serializer.fromJson<String>(json['code']),
json: serializer.fromJson<String>(json['json']),
);
}
@@ -480,20 +481,20 @@ class NotificationPermissionDb extends DataClass
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
- 'id': serializer.toJson<int>(id),
+ 'code': serializer.toJson<String>(code),
'json': serializer.toJson<String>(json),
};
}
- NotificationPermissionDb copyWith({int? id, String? json}) =>
+ NotificationPermissionDb copyWith({String? code, String? json}) =>
NotificationPermissionDb(
- id: id ?? this.id,
+ code: code ?? this.code,
json: json ?? this.json,
);
NotificationPermissionDb copyWithCompanion(
NotificationPermissionEntityCompanion data) {
return NotificationPermissionDb(
- id: data.id.present ? data.id.value : this.id,
+ code: data.code.present ? data.code.value : this.code,
json: data.json.present ? data.json.value : this.json,
);
}
@@ -501,54 +502,54 @@ class NotificationPermissionDb extends DataClass
@override
String toString() {
return (StringBuffer('NotificationPermissionDb(')
- ..write('id: $id, ')
+ ..write('code: $code, ')
..write('json: $json')
..write(')'))
.toString();
}
@override
- int get hashCode => Object.hash(id, json);
+ int get hashCode => Object.hash(code, json);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is NotificationPermissionDb &&
- other.id == this.id &&
+ other.code == this.code &&
other.json == this.json);
}
class NotificationPermissionEntityCompanion
extends UpdateCompanion<NotificationPermissionDb> {
- final Value<int> id;
+ final Value<String> code;
final Value<String> json;
final Value<int> rowid;
const NotificationPermissionEntityCompanion({
- this.id = const Value.absent(),
+ this.code = const Value.absent(),
this.json = const Value.absent(),
this.rowid = const Value.absent(),
});
NotificationPermissionEntityCompanion.insert({
- required int id,
+ required String code,
required String json,
this.rowid = const Value.absent(),
- }) : id = Value(id),
+ }) : code = Value(code),
json = Value(json);
static Insertable<NotificationPermissionDb> custom({
- Expression<int>? id,
+ Expression<String>? code,
Expression<String>? json,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
- if (id != null) 'id': id,
+ if (code != null) 'code': code,
if (json != null) 'json': json,
if (rowid != null) 'rowid': rowid,
});
}
NotificationPermissionEntityCompanion copyWith(
- {Value<int>? id, Value<String>? json, Value<int>? rowid}) {
+ {Value<String>? code, Value<String>? json, Value<int>? rowid}) {
return NotificationPermissionEntityCompanion(
- id: id ?? this.id,
+ code: code ?? this.code,
json: json ?? this.json,
rowid: rowid ?? this.rowid,
);
@@ -557,8 +558,8 @@ class NotificationPermissionEntityCompanion
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
- if (id.present) {
- map['id'] = Variable<int>(id.value);
+ if (code.present) {
+ map['code'] = Variable<String>(code.value);
}
if (json.present) {
map['json'] = Variable<String>(json.value);
@@ -572,7 +573,7 @@ class NotificationPermissionEntityCompanion
@override
String toString() {
return (StringBuffer('NotificationPermissionEntityCompanion(')
- ..write('id: $id, ')
+ ..write('code: $code, ')
..write('json: $json, ')
..write('rowid: $rowid')
..write(')'))
@@ -1246,13 +1247,13 @@ typedef $$UserEntityTableProcessedTableManager = ProcessedTableManager<
PrefetchHooks Function()>;
typedef $$NotificationPermissionEntityTableCreateCompanionBuilder
= NotificationPermissionEntityCompanion Function({
- required int id,
+ required String code,
required String json,
Value<int> rowid,
});
typedef $$NotificationPermissionEntityTableUpdateCompanionBuilder
= NotificationPermissionEntityCompanion Function({
- Value<int> id,
+ Value<String> code,
Value<String> json,
Value<int> rowid,
});
@@ -1266,8 +1267,8 @@ class $$NotificationPermissionEntityTableFilterComposer
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
- ColumnFilters<int> get id => $composableBuilder(
- column: $table.id, builder: (column) => ColumnFilters(column));
+ ColumnFilters<String> get code => $composableBuilder(
+ column: $table.code, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get json => $composableBuilder(
column: $table.json, builder: (column) => ColumnFilters(column));
@@ -1282,8 +1283,8 @@ class $$NotificationPermissionEntityTableOrderingComposer
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
- ColumnOrderings<int> get id => $composableBuilder(
- column: $table.id, builder: (column) => ColumnOrderings(column));
+ ColumnOrderings<String> get code => $composableBuilder(
+ column: $table.code, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get json => $composableBuilder(
column: $table.json, builder: (column) => ColumnOrderings(column));
@@ -1298,8 +1299,8 @@ class $$NotificationPermissionEntityTableAnnotationComposer
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
- GeneratedColumn<int> get id =>
- $composableBuilder(column: $table.id, builder: (column) => column);
+ GeneratedColumn<String> get code =>
+ $composableBuilder(column: $table.code, builder: (column) => column);
GeneratedColumn<String> get json =>
$composableBuilder(column: $table.json, builder: (column) => column);
@@ -1336,22 +1337,22 @@ class $$NotificationPermissionEntityTableTableManager extends RootTableManager<
$$NotificationPermissionEntityTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
- Value<int> id = const Value.absent(),
+ Value<String> code = const Value.absent(),
Value<String> json = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) =>
NotificationPermissionEntityCompanion(
- id: id,
+ code: code,
json: json,
rowid: rowid,
),
createCompanionCallback: ({
- required int id,
+ required String code,
required String json,
Value<int> rowid = const Value.absent(),
}) =>
NotificationPermissionEntityCompanion.insert(
- id: id,
+ code: code,
json: json,
rowid: rowid,
),
diff --git a/comwell_key_app/lib/.generated/notifications/models/notification_permission.g.dart b/comwell_key_app/lib/.generated/notifications/models/notification_permission.g.dart
index c4aafa0e..42020cb3 100644
--- a/comwell_key_app/lib/.generated/notifications/models/notification_permission.g.dart
+++ b/comwell_key_app/lib/.generated/notifications/models/notification_permission.g.dart
@@ -8,19 +8,19 @@ part of '../../../notifications/models/notification_permission.dart';
NotificationPermission _$NotificationPermissionFromJson(Map json) =>
NotificationPermission(
- id: (json['id'] as num).toInt(),
- name: json['name'] as String,
+ code: json['code'] as String,
+ displayName: json['displayName'] as String,
notificationPermissionDescription:
json['notificationPermissionDescription'] as String?,
- allowed: json['allowed'] as bool,
+ given: json['given'] as bool,
);
Map<String, dynamic> _$NotificationPermissionToJson(
NotificationPermission instance) =>
<String, dynamic>{
- 'id': instance.id,
- 'name': instance.name,
+ 'code': instance.code,
+ 'displayName': instance.displayName,
'notificationPermissionDescription':
instance.notificationPermissionDescription,
- 'allowed': instance.allowed,
+ 'given': instance.given,
};
diff --git a/comwell_key_app/lib/.generated/overview/models/guest.g.dart b/comwell_key_app/lib/.generated/overview/models/guest.g.dart
deleted file mode 100644
index d09b6d96..00000000
--- a/comwell_key_app/lib/.generated/overview/models/guest.g.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-part of '../../../overview/models/guest.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Guest _$GuestFromJson(Map json) => Guest(
- name: json['name'] as String,
- id: json['id'] as String,
- );
-
-Map<String, dynamic> _$GuestToJson(Guest instance) => <String, dynamic>{
- 'name': instance.name,
- 'id': instance.id,
- };
diff --git a/comwell_key_app/lib/.generated/services/models/booking_dto.g.dart b/comwell_key_app/lib/.generated/services/models/booking_dto.g.dart
index 4b118d0e..aa55aa6a 100644
--- a/comwell_key_app/lib/.generated/services/models/booking_dto.g.dart
+++ b/comwell_key_app/lib/.generated/services/models/booking_dto.g.dart
@@ -11,10 +11,16 @@ BookingDTO _$BookingDTOFromJson(Map json) => BookingDTO(
hotelCode: json['hotelCode'] as String,
firstName: json['firstName'] as String,
lastName: json['lastName'] as String,
+ bookerFirstName: json['bookerFirstName'] as String? ?? '',
+ bookerLastName: json['bookerLastName'] as String? ?? '',
+ guests: (json['guests'] as List<dynamic>?)
+ ?.map(
+ (e) => GuestDTO.fromJson(Map<String, dynamic>.from(e as Map)))
+ .toList() ??
+ const [],
confirmationNumber: json['confirmationNumber'] as String,
- hmsConfirmationNumber: json['hmsConfirmationNumber'] as String,
dayIn: json['dayIn'] as String,
- dayOut: json['dayOut'] as String?,
+ dayOut: json['dayOut'] as String,
cancelTime: json['cancelTime'] as String?,
status: json['status'] as String,
isCancelled: json['isCancelled'] as bool?,
@@ -22,13 +28,13 @@ BookingDTO _$BookingDTOFromJson(Map json) => BookingDTO(
roomType: json['roomType'] as String?,
adults: (json['adults'] as num).toInt(),
children: (json['children'] as num).toInt(),
- totalCharge: json['totalCharge'] as num?,
balance: json['balance'] as num?,
- maskedCardNumber: json['maskedCardNumber'] as String?,
+ isPrimaryGuest: json['isPrimaryGuest'] as bool,
addOnItems: (json['addOnItems'] as List<dynamic>?)
?.map((e) =>
BookingAddonItem.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
+ maskedCardNumber: json['maskedCardNumber'] as String?,
);
Map<String, dynamic> _$BookingDTOToJson(BookingDTO instance) =>
@@ -37,8 +43,10 @@ Map<String, dynamic> _$BookingDTOToJson(BookingDTO instance) =>
'hotelCode': instance.hotelCode,
'firstName': instance.firstName,
'lastName': instance.lastName,
+ 'bookerFirstName': instance.bookerFirstName,
+ 'bookerLastName': instance.bookerLastName,
+ 'guests': instance.guests.map((e) => e.toJson()).toList(),
'confirmationNumber': instance.confirmationNumber,
- 'hmsConfirmationNumber': instance.hmsConfirmationNumber,
'dayIn': instance.dayIn,
'dayOut': instance.dayOut,
'cancelTime': instance.cancelTime,
@@ -48,12 +56,24 @@ Map<String, dynamic> _$BookingDTOToJson(BookingDTO instance) =>
'roomType': instance.roomType,
'adults': instance.adults,
'children': instance.children,
- 'totalCharge': instance.totalCharge,
'balance': instance.balance,
+ 'isPrimaryGuest': instance.isPrimaryGuest,
'maskedCardNumber': instance.maskedCardNumber,
'addOnItems': instance.addOnItems?.map((e) => e.toJson()).toList(),
};
+GuestDTO _$GuestDTOFromJson(Map json) => GuestDTO(
+ id: (json['id'] as num).toInt(),
+ firstName: json['firstName'] as String,
+ lastName: json['lastName'] as String,
+ );
+
+Map<String, dynamic> _$GuestDTOToJson(GuestDTO instance) => <String, dynamic>{
+ 'id': instance.id,
+ 'firstName': instance.firstName,
+ 'lastName': instance.lastName,
+ };
+
BookingAddonItem _$BookingAddonItemFromJson(Map json) => BookingAddonItem(
json['code'] as String,
json['description'] as String,
diff --git a/comwell_key_app/lib/.generated/services/models/user_dto.g.dart b/comwell_key_app/lib/.generated/services/models/user_dto.g.dart
index 07e895ea..35682a59 100644
--- a/comwell_key_app/lib/.generated/services/models/user_dto.g.dart
+++ b/comwell_key_app/lib/.generated/services/models/user_dto.g.dart
@@ -8,34 +8,57 @@ part of '../../../services/models/user_dto.dart';
UserDto _$UserDtoFromJson(Map json) => UserDto(
id: (json['id'] as num).toInt(),
+ userId: json['userId'] as String? ?? '',
+ hmsId: json['hmsId'] as String?,
firstName: json['firstName'] as String,
lastName: json['lastName'] as String,
email: json['email'] as String,
- clubId: json['clubId'] as String?,
- clubLevel: json['clubLevel'] as String?,
- clubLevelName: json['clubLevelName'] as String?,
+ emailVerified: json['emailVerified'] as bool,
+ isClubMember: json['isClubMember'] as bool,
birthDate: json['birthDate'] == null
? null
: DateTime.parse(json['birthDate'] as String),
- phoneNumber: json['phoneNumber'] as String?,
+ createDate: json['createDate'] == null
+ ? null
+ : DateTime.parse(json['createDate'] as String),
+ companyId: json['companyId'] as String?,
+ companyName: json['companyName'] as String?,
+ symplifyId: json['symplifyId'] as String?,
+ phoneNumber: json['phoneNumber'] as String? ?? '',
gender: json['gender'] as String?,
- addressStreet: json['addressStreet'] as String?,
- addressZip: json['addressZip'] as String?,
- addressCity: json['addressCity'] as String?,
- addressCountry: json['addressCountry'] as String?,
- points: (json['points'] as num?)?.toInt(),
- locale: json['locale'] as String?,
+ addressStreet: json['addressStreet'] as String? ?? '',
+ addressZip: json['addressZip'] as String? ?? '',
+ addressCity: json['addressCity'] as String? ?? '',
+ addressCountry: json['addressCountry'] as String? ?? '',
+ points: (json['points'] as num).toInt(),
+ signUpDate: json['signUpDate'] == null
+ ? null
+ : DateTime.parse(json['signUpDate'] as String),
+ signUpCampaign: json['signUpCampaign'] as String?,
+ signUpSource: json['signUpSource'] as String?,
+ locale: json['locale'] as String? ?? '',
+ wasRecentlyCreated: json['wasRecentlyCreated'] as bool,
+ permissions: json['permissions'] == null
+ ? null
+ : UserPermissions.fromJson(
+ Map<String, dynamic>.from(json['permissions'] as Map)),
+ nationality: json['nationality'] as String?,
);
Map<String, dynamic> _$UserDtoToJson(UserDto instance) => <String, dynamic>{
'id': instance.id,
+ 'userId': instance.userId,
+ 'hmsId': instance.hmsId,
'firstName': instance.firstName,
'lastName': instance.lastName,
'email': instance.email,
- 'clubId': instance.clubId,
- 'clubLevel': instance.clubLevel,
- 'clubLevelName': instance.clubLevelName,
+ 'emailVerified': instance.emailVerified,
+ 'isClubMember': instance.isClubMember,
'birthDate': instance.birthDate?.toIso8601String(),
+ 'createDate': instance.createDate?.toIso8601String(),
+ 'companyId': instance.companyId,
+ 'companyName': instance.companyName,
+ 'symplifyId': instance.symplifyId,
'phoneNumber': instance.phoneNumber,
'gender': instance.gender,
'addressStreet': instance.addressStreet,
@@ -43,5 +66,11 @@ Map<String, dynamic> _$UserDtoToJson(UserDto instance) => <String, dynamic>{
'addressCity': instance.addressCity,
'addressCountry': instance.addressCountry,
'points': instance.points,
+ 'signUpDate': instance.signUpDate?.toIso8601String(),
+ 'signUpCampaign': instance.signUpCampaign,
+ 'signUpSource': instance.signUpSource,
'locale': instance.locale,
+ 'wasRecentlyCreated': instance.wasRecentlyCreated,
+ 'permissions': instance.permissions?.toJson(),
+ 'nationality': instance.nationality,
};
diff --git a/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart b/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart
new file mode 100644
index 00000000..e8c28615
--- /dev/null
+++ b/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart
@@ -0,0 +1,22 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../services/models/user_permissions.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+UserPermissions _$UserPermissionsFromJson(Map json) => UserPermissions(
+ guestOptIn1: json['GuestOptIn1'] as bool,
+ guestOptIn2: json['GuestOptIn2'] as bool,
+ guestOptIn3: json['GuestOptIn3'] as bool,
+ guestOptIn4: json['GuestOptIn4'] as bool,
+ );
+
+Map<String, dynamic> _$UserPermissionsToJson(UserPermissions instance) =>
+ <String, dynamic>{
+ 'GuestOptIn1': instance.guestOptIn1,
+ 'GuestOptIn2': instance.guestOptIn2,
+ 'GuestOptIn3': instance.guestOptIn3,
+ 'GuestOptIn4': instance.guestOptIn4,
+ };
diff --git a/comwell_key_app/lib/authentication/authentication_repository.dart b/comwell_key_app/lib/authentication/authentication_repository.dart
index a7686335..f2ad620d 100644
--- a/comwell_key_app/lib/authentication/authentication_repository.dart
+++ b/comwell_key_app/lib/authentication/authentication_repository.dart
@@ -26,14 +26,11 @@ class AuthenticationRepository {
late final String configFilePath;
late final String authorityUrl;
late final MultipleAccountPca msAuth;
- final scopes = dotenv.env['ENTRA_API_URL']!.split( ',');
-
+ final scopes = dotenv.env['ENTRA_API_URL']!.split(',');
Future<void> init() async {
final clientId = dotenv.env["ENTRA_ID_CLIENT_ID"]!;
- final redirect = dotenv
- .env["ENTRA_ID_REDIRECT_URL"]!;
-
+ final redirect = dotenv.env["ENTRA_ID_REDIRECT_URL"]!;
switch (appFlavor?.toLowerCase()) {
case "develop":
@@ -76,14 +73,9 @@ class AuthenticationRepository {
);
}
- Stream<AuthenticationStatus> get status {
- return _controller.stream.map((status) {
- _onAuthResult(status);
- return status;
- });
- }
+ Stream<AuthenticationStatus> get status => _controller.stream;
- void _onAuthResult(AuthenticationStatus status) async {
+ Future<void> _onAuthResult(AuthenticationStatus status) async {
try {
if (status == AuthenticationStatus.authenticated) {
await seos.startMobilePlugin();
@@ -94,7 +86,6 @@ class AuthenticationRepository {
}
Future<void> logIn() async {
- _controller.sink.add(AuthenticationStatus.authenticated);
await FirebaseAnalytics.instance.logLogin();
await FirebaseAnalytics.instance.setUserProperty(
name: 'login_status',
@@ -106,15 +97,31 @@ class AuthenticationRepository {
} catch (e) {
// no op
}
+
+ // Emit status and trigger side effects
+ _emitStatus(AuthenticationStatus.authenticated);
+ }
+
+ void _emitStatus(AuthenticationStatus status) {
+ _controller.sink.add(status);
+ _onAuthResult(status);
}
Future<void> logOut({bool forced = false}) async {
try {
- // Clear local storage first
- await secureStorage.deleteAll();
- await database.deleteDatabase();
-
+ // Delete database BEFORE deleting cipher key from secure storage
+ // The cipher key is needed to decrypt the database
+ try {
+ await seos.terminateEndpoint();
+ await database.deleteDatabase();
+ } catch (e) {
+ debugPrint("Error deleting database during logout: $e");
+ // Continue with logout even if database deletion fails
+ }
+ // Clear local storage (including cipher key) after database is deleted
+ await secureStorage.deleteAll();
+
// await msAuth.removeAccount();
// Track the logout event
@@ -125,22 +132,22 @@ class AuthenticationRepository {
);
// Update authentication status
- if (forced) {
- _controller.sink.add(AuthenticationStatus.forcedUnauthenticated);
- } else {
- _controller.sink.add(AuthenticationStatus.unauthenticated);
- }
+ _emitStatus(forced
+ ? AuthenticationStatus.forcedUnauthenticated
+ : AuthenticationStatus.unauthenticated);
} catch (e) {
debugPrint("Error during logout: $e");
// Even if logout fails, still clear local data and update status
+ try {
+ await database.deleteDatabase();
+ } catch (dbError) {
+ debugPrint("Error deleting database in catch block: $dbError");
+ }
await secureStorage.deleteAll();
- await database.deleteDatabase();
- if (forced) {
- _controller.sink.add(AuthenticationStatus.forcedUnauthenticated);
- } else {
- _controller.sink.add(AuthenticationStatus.unauthenticated);
- }
+ _emitStatus(forced
+ ? AuthenticationStatus.forcedUnauthenticated
+ : AuthenticationStatus.unauthenticated);
}
}
@@ -176,33 +183,48 @@ class AuthenticationRepository {
Future<void> loginWithCode(AuthenticationResult code) async {
await secureStorage.write(constants.accessToken, code.accessToken);
await secureStorage.write(constants.identifier, code.account.id);
- await secureStorage.write(constants.correlationId, code.correlationId ?? '');
+ await secureStorage.write(
+ constants.correlationId, code.correlationId ?? '');
await logIn();
}
Future<String> get accessToken async {
try {
//First check if we have a token in secure storage
- final storedToken = await secureStorage.read(constants.accessToken);
+ final storedToken = await secureStorage.read(constants.accessToken);
if (storedToken != null && storedToken.isNotEmpty) {
return storedToken;
- }
+ }
// If no stored token, try to get a fresh one silently
- return (await msAuth.acquireTokenSilent(scopes: scopes, authority: authorityUrl)).accessToken;
+ return (await msAuth.acquireTokenSilent(
+ scopes: scopes, authority: authorityUrl))
+ .accessToken;
} catch (e) {
throw Exception('No valid access token available');
}
}
+
Future<AuthenticationResult> acquireTokenSilent(String identifier) async {
-
- // Use the first account's identifier
+ if (identifier.isEmpty) {
+ throw Exception('Cannot acquire token: identifier is empty');
+ }
- return await msAuth.acquireTokenSilent(
- scopes: scopes,
- authority: authorityUrl,
- identifier: identifier,
-
- );
+ try {
+ debugPrint('Acquiring token silently for identifier: ${identifier.substring(0, 8)}...');
+ final result = await msAuth.acquireTokenSilent(
+ scopes: scopes,
+ authority: authorityUrl,
+ identifier: identifier,
+ );
+ debugPrint('Token acquired successfully');
+ return result;
+ } on PlatformException catch (e) {
+ debugPrint('PlatformException during silent token acquisition: ${e.code} - ${e.message}');
+ rethrow;
+ } catch (e) {
+ debugPrint('Error during silent token acquisition: $e');
+ rethrow;
+ }
}
}
diff --git a/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart b/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart
index cedd219b..c22f38e7 100644
--- a/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart
+++ b/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart
@@ -6,6 +6,7 @@ import 'package:comwell_key_app/housekeeping/models/housekeeping.dart';
import 'package:comwell_key_app/overview/models/guest.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile_settings/model/user.dart';
+import 'package:comwell_key_app/share/share_booking_repository.dart';
import 'package:comwell_key_app/up_sales/models/up_sales.dart';
import 'package:comwell_key_app/up_sales/models/upgrade.dart';
import 'package:comwell_key_app/up_sales/up_sales_repository.dart';
@@ -25,7 +26,7 @@ part 'booking_details_state.dart';
class BookingDetailsBloc
extends Bloc<BookingDetailsEvent, BookingDetailsState> {
late Booking booking;
- late User user;
+ User? user;
Timer? _timer;
final BookingDetailsRepository bookingDetailsRepository;
final ProfileRepository profileRepository;
@@ -33,7 +34,8 @@ class BookingDetailsBloc
Duration _remainingTime = Duration.zero;
final UpSalesRepository upSaleRepository;
final HouseKeepingRepository houseKeepingRepository;
-
+ final ShareBookingRepository shareBookingRepository =
+ locator<ShareBookingRepository>();
BookingDetailsBloc(
this.booking, {
required this.bookingDetailsRepository,
@@ -44,14 +46,15 @@ class BookingDetailsBloc
on<InitialEvent>((event, emit) async {
try {
emit(state.loading());
-
+ debugPrint("booking=$booking");
+ add(GetUserEvent(event.fetchRemote));
add(StartTimerEvent());
add(CheckIfHouseKeepingOrdered());
add(CheckMobileKeys());
- add(GetBookingDetailsEvent(booking.hmsConfirmationNumber));
- add(GetUserEvent());
+ add(GetBookingDetailsEvent(booking.confirmationNumber, event.fetchRemote));
add(UpdateRemainingEvent(getCheckInTime().difference(DateTime.now())));
add(GetUpSalesEvent());
+ emit(state.loaded());
} catch (e, st) {
if (kDebugMode) print("err=$e, $st");
emit(state.setupError());
@@ -60,8 +63,18 @@ class BookingDetailsBloc
on<PreregisterEvent>((event, emit) async {
emit(state.loading());
- await getBookingDetails(emit, booking.hmsConfirmationNumber, preregister: true, hotelCode: booking.hotelCode);
- await getUpSales(emit, preregister: true);
+ await getBookingDetails(emit, booking.confirmationNumber,
+ fetchRemote: true, hotelCode: booking.hotelCode);
+ await getUpSales(emit, fetchRemote: true);
+ emit(state.main());
+ });
+
+ on<CheckInEvent>((event, emit) async {
+ emit(state.loading());
+ await getBookingDetails(emit, booking.confirmationNumber,
+ fetchRemote: true, hotelCode: booking.hotelCode);
+ await checkMobileKeys(emit);
+ await getUpSales(emit, fetchRemote: true);
emit(state.main());
});
@@ -100,24 +113,25 @@ class BookingDetailsBloc
});
on<GetBookingDetailsEvent>((event, emit) async {
- await getBookingDetails(emit, event.hmsConfirmationNumber, hotelCode: booking.hotelCode);
+ await getBookingDetails(emit, event.hmsConfirmationNumber,
+ hotelCode: booking.hotelCode, fetchRemote: event.fetchRemote);
});
on<GetUserEvent>((event, emit) async {
- await getUser(emit);
+ await getUser(emit, fetchRemote: event.fetchRemote);
});
}
Future<void> getUpSales(Emitter<BookingDetailsState> emit,
- {bool preregister = false}) async {
+ {bool fetchRemote = false}) async {
try {
- if (preregister) {
+ if (fetchRemote) {
final response = await upSaleRepository.getRemoteUpSales(
- booking.hmsConfirmationNumber, booking.hotelCode);
+ booking.confirmationNumber, booking.hotelCode);
emit(state.getUpSales(response));
} else {
final response = await upSaleRepository.getUpSales(
- booking.hmsConfirmationNumber, booking.hotelCode);
+ booking.confirmationNumber, booking.hotelCode);
emit(state.getUpSales(response));
}
} catch (e) {
@@ -128,17 +142,18 @@ class BookingDetailsBloc
Future<Booking> getBookingDetails(
Emitter<BookingDetailsState> emit, String hmsConfirmationNumber,
- {bool preregister = false, required String hotelCode}) async {
+ {bool fetchRemote = false, required String hotelCode}) async {
emit(state.loading());
try {
- if (preregister) {
- final bookingDetails =
- await profileRepository.getRemoteBookingDetails(hmsConfirmationNumber, hotelCode);
+ if (fetchRemote) {
+ final bookingDetails = await bookingDetailsRepository.getRemoteBookingDetails(
+ hmsConfirmationNumber, hotelCode);
booking = bookingDetails;
return booking;
} else {
- final bookingDetails =
- await profileRepository.getBookingDetails(hmsConfirmationNumber, hotelCode);
+ user = await profileRepository.fetchProfileSettings();
+ final bookingDetails = await bookingDetailsRepository.getBookingDetails(
+ hmsConfirmationNumber, hotelCode);
booking = bookingDetails;
return booking;
@@ -256,7 +271,9 @@ class BookingDetailsBloc
bool get isHouseKeepingTime =>
booking.endDate.difference(booking.startDate).inDays >= 2;
- Future<void> getUser(Emitter<BookingDetailsState> emit) async {
- user = await profileRepository.fetchProfileSettings();
+ Future<void> getUser(Emitter<BookingDetailsState> emit,
+ {bool fetchRemote = false}) async {
+ user =
+ await profileRepository.fetchProfileSettings(fetchRemote: fetchRemote);
}
}
diff --git a/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart b/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart
index c78e006a..90a676d4 100644
--- a/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart
+++ b/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart
@@ -7,28 +7,43 @@ sealed class BookingDetailsEvent extends Equatable {
List<Object> get props => [];
}
-final class InitialEvent extends BookingDetailsEvent {}
+final class InitialEvent extends BookingDetailsEvent {
+ final bool fetchRemote;
-final class GetUpSalesEvent extends BookingDetailsEvent {
-}
+ const InitialEvent({this.fetchRemote = false});
-final class PreregisterEvent extends BookingDetailsEvent {
+ @override
+ List<Object> get props => [fetchRemote];
}
+final class GetUpSalesEvent extends BookingDetailsEvent {}
+
+final class PreregisterEvent extends BookingDetailsEvent {}
+
+final class CheckInEvent extends BookingDetailsEvent {}
+
final class CheckIfHouseKeepingOrdered extends BookingDetailsEvent {}
final class GetBookingDetailsEvent extends BookingDetailsEvent {
final String hmsConfirmationNumber;
+ final bool fetchRemote;
- const GetBookingDetailsEvent(this.hmsConfirmationNumber);
+ const GetBookingDetailsEvent(this.hmsConfirmationNumber, this.fetchRemote);
@override
- List<Object> get props => [hmsConfirmationNumber];
+ List<Object> get props => [hmsConfirmationNumber, fetchRemote];
}
final class StartTimerEvent extends BookingDetailsEvent {}
-final class GetUserEvent extends BookingDetailsEvent {}
+final class GetUserEvent extends BookingDetailsEvent {
+ final bool fetchRemote;
+
+ const GetUserEvent(this.fetchRemote);
+
+ @override
+ List<Object> get props => [fetchRemote];
+}
final class CheckMobileKeys extends BookingDetailsEvent {}
@@ -66,4 +81,4 @@ final class OrderHouseKeepingEvent extends BookingDetailsEvent {
@override
List<Object> get props => [selectedServices];
-}
\ No newline at end of file
+}
diff --git a/comwell_key_app/lib/booking_details/bloc/booking_details_state.dart b/comwell_key_app/lib/booking_details/bloc/booking_details_state.dart
index d8b6b516..de0409d2 100644
--- a/comwell_key_app/lib/booking_details/bloc/booking_details_state.dart
+++ b/comwell_key_app/lib/booking_details/bloc/booking_details_state.dart
@@ -42,6 +42,7 @@ class BookingDetailsState extends Equatable {
BookingDetailsState updateGuests(Iterable<Guest> guests) =>
copyWith(status: BookingDetailsStatus.guestsUpdated, guests: guests);
BookingDetailsState loading() => copyWith(status: BookingDetailsStatus.loading, isLoading: true);
+ BookingDetailsState loaded() => copyWith(status: BookingDetailsStatus.loaded, isLoading: false);
BookingDetailsState updateRemainingTime(Duration remainingTime) => copyWith(remainingTime: remainingTime);
BookingDetailsState main() => copyWith(status: BookingDetailsStatus.main, isLoading: false);
BookingDetailsState getUpSales(UpSales upSales) => copyWith(upSales: upSales);
@@ -82,6 +83,7 @@ class BookingDetailsState extends Equatable {
enum BookingDetailsStatus {
initial,
+ loaded,
setupError,
keysUpdated,
guestsUpdated,
diff --git a/comwell_key_app/lib/booking_details/booking_details_page.dart b/comwell_key_app/lib/booking_details/booking_details_page.dart
index 361b764d..6e5cb147 100644
--- a/comwell_key_app/lib/booking_details/booking_details_page.dart
+++ b/comwell_key_app/lib/booking_details/booking_details_page.dart
@@ -10,7 +10,6 @@ import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
@@ -29,7 +28,7 @@ class BookingDetailsPage extends StatelessWidget {
final cubit = context.read<BookingDetailsBloc>();
if (state.status == BookingDetailsStatus.initial) {
- cubit.add(InitialEvent());
+ cubit.add(const InitialEvent(fetchRemote: true));
}
return Scaffold(
@@ -79,9 +78,14 @@ class BookingDetailsPage extends StatelessWidget {
Stack(
alignment: Alignment.center,
children: [
- SafeArea(
- child: ShareButton(guests: state.guests),
- ),
+ if (cubit.booking.reservationStatus ==
+ ReservationStatus.preregistered ||
+ cubit.booking.reservationStatus ==
+ ReservationStatus.preregistered ||
+ cubit.booking.reservationStatus == ReservationStatus.checkedin)
+ SafeArea(
+ child: cubit.booking.isPrimaryGuest &&cubit.booking.reservationStatus == ReservationStatus.checkedin ? ShareButton(guests: state.guests) : const SizedBox(),
+ ),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
@@ -95,18 +99,21 @@ class BookingDetailsPage extends StatelessWidget {
child: Divider(color: colorDivider),
),
const SizedBox(height: 10),
- if (state.keys.isNotEmpty && cubit.booking.roomNumber != '') //&& state.keys.where((key) => key.label == cubit.booking.roomNumber))
- const UnlockRoomButton()
- else if (state.keys.isEmpty &&
- cubit.booking.reservationStatus ==
- ReservationStatus.checkedin)
+ if (
+ state.keys.isNotEmpty &&
+ cubit.booking.roomNumber != '' &&
+ state.keys.any((key) => key.label == cubit.booking.roomNumber)
+ )
+ UnlockRoomButton(roomNumber: cubit.booking.roomNumber)
+ else if (cubit.booking.reservationStatus == ReservationStatus.checkedin
+ )
const GetKeysButton()
else if (cubit.booking.reservationStatus ==
ReservationStatus.preregistered)
const CheckInButtonTimer()
else if (cubit.booking.reservationStatus ==
ReservationStatus.newreservation)
- const PreregisterButton()
+ const PreregisterButton()
else
const SizedBox(),
const SizedBox(
@@ -126,7 +133,7 @@ class BookingDetailsPage extends StatelessWidget {
return InkWell(
onTap: () async {
await context.pushNamed(AppRoutes.myBooking.name, extra: cubit.booking);
- cubit.add(InitialEvent());
+ cubit.add(const InitialEvent(fetchRemote: true));
},
child: Container(
width: double.infinity,
@@ -151,7 +158,7 @@ class BookingDetailsPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- cubit.booking.id,
+ cubit.booking.confirmationNumber,
style: theme.textTheme.labelLarge
?.copyWith(color: colorBackground),
),
@@ -163,24 +170,6 @@ class BookingDetailsPage extends StatelessWidget {
),
],
),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- 'balance'.tr(),
- textAlign: TextAlign.end,
- style: theme.textTheme.labelLarge
- ?.copyWith(color: colorBackground),
- ),
- Text(
- 'total_charge_value'
- .tr(args: [cubit.trimmedBalance]),
- style: theme.textTheme.headlineMedium?.copyWith(
- color: colorBackground,
- ),
- ),
- ],
- ),
],
),
],
diff --git a/comwell_key_app/lib/booking_details/booking_details_repository.dart b/comwell_key_app/lib/booking_details/booking_details_repository.dart
index 530e8f2c..fba8efa1 100644
--- a/comwell_key_app/lib/booking_details/booking_details_repository.dart
+++ b/comwell_key_app/lib/booking_details/booking_details_repository.dart
@@ -1,16 +1,67 @@
+import 'package:comwell_key_app/database/comwell_db.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/services/api.dart';
+import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
+import 'package:comwell_key_app/services/models/bookings_dto.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/secure_storage.dart';
+import 'package:flutter/widgets.dart';
class BookingDetailsRepository {
final api = Api();
final secureStorage = locator<SecureStorage>();
+ Future<String?> doesInvitationCodeExist(String key) async {
+ return await secureStorage.read(key);
+ }
+ Future<Booking> getRemoteBookingDetails(
+ String hmsConfirmationNumber, String hotelCode) async {
+ final response =
+ await api.getBookingDetails(hmsConfirmationNumber, hotelCode);
+ return response!.toBooking();
+ }
+ Future<Booking> getBookingDetails(
+ String hmsConfirmationNumber, String hotelCode) async {
- Future<String?> doesInvitationCodeExist(String key) async {
- return await secureStorage.read(key);
+
+ final booking = await _checkIfBookingDetailsExists(hmsConfirmationNumber);
+ if (booking != null) {
+ return booking;
+ }
+
+ final newBooking = await _fetchAndSaveBookingDetailsToDatabase(
+ hmsConfirmationNumber, hotelCode);
+ return newBooking;
}
+ Future<Booking?> _checkIfBookingDetailsExists(
+ String hmsConfirmationNumber) async {
+ try {
+ final booking = await locator<ComwellDatabase>()
+ .bookingsDao
+ .getBookingDetails(hmsConfirmationNumber);
+ return booking;
+ } catch (e) {
+ debugPrint("Error checking if booking details exists: $e");
+ return null;
+ }
+ }
+
+ Future<Booking> _fetchAndSaveBookingDetailsToDatabase(
+ String hmsConfirmationNumber, String hotelCode) async {
+ try {
+ final response =
+ await api.getBookingDetails(hmsConfirmationNumber, hotelCode);
+ await locator<ComwellDatabase>().bookingsDao.insertBookings(
+ BookingsDTO(current: [response!], past: [], cancelled: []));
+ final booking = response.toBooking();
+ debugPrint("Booking saved to database: ${booking.confirmationNumber}");
+ return booking;
+ } on Exception catch (e) {
+ debugPrint("Error fetching booking details: $e");
+ rethrow;
+ }
+ }
}
diff --git a/comwell_key_app/lib/booking_details/components/booking_details_bottom_sheet.dart b/comwell_key_app/lib/booking_details/components/booking_details_bottom_sheet.dart
index 636be348..7c36701d 100644
--- a/comwell_key_app/lib/booking_details/components/booking_details_bottom_sheet.dart
+++ b/comwell_key_app/lib/booking_details/components/booking_details_bottom_sheet.dart
@@ -6,6 +6,7 @@ import 'package:comwell_key_app/common/components/bottom_sheet_widget.dart';
import 'package:comwell_key_app/common/components/comwell_error_widget.dart';
import 'package:comwell_key_app/common/components/outlined_pill_button.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/profile/components/comwell_club_container.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/up_sales/components/catalog/service_catalog.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -21,16 +22,34 @@ class BookingDetailsBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
+ final isActive =
+ cubit.user?.isClubMember ?? false;
+
return BottomSheetWidget(
widgetChildren: [
const SizedBox(height: 16),
- cubit.booking.reservationStatus == ReservationStatus.checkedin
- //&&cubit.getCheckOutTime().isBefore(DateTime.now()) TODO: Add this back in
+ cubit.booking.reservationStatus == ReservationStatus.checkedin &&
+ cubit.getCheckOutTime().isBefore(DateTime.now())
? const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: CheckOutButton(),
)
: const SizedBox(),
+ cubit.booking.reservationStatus == ReservationStatus.checkedin &&
+ cubit.isHouseKeepingTime
+ ? Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: HousekeepingButton(
+ key: ValueKey(state.isHouseKeepingOrdered),
+ booking: cubit.booking),
+ ),
+ ],
+ )
+ : const SizedBox(),
const SizedBox(height: 20),
_buildUpSalesCatalogButton(cubit.booking.reservationStatus, context),
const SizedBox(height: 20),
@@ -80,40 +99,27 @@ class BookingDetailsBottomSheet extends StatelessWidget {
),
]),
const SizedBox(height: 16),
- if (cubit.booking.rooms.length > 1)
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: Text('rooms'.tr(), style: theme.textTheme.headlineMedium),
- ),
- const SizedBox(height: 16),
- if (state.keys.isNotEmpty)
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child:
- Text('room_keys'.tr(), style: theme.textTheme.headlineMedium),
- ),
+ if (!isActive && cubit.user != null)
+ ComwellClubContainer(
+ user: cubit.user!,
+ onSignupClick: () {
+ cubit.add(const InitialEvent(fetchRemote: true));
+ }),
const SizedBox(height: 16),
- cubit.booking.reservationStatus == ReservationStatus.checkedin &&
- cubit.isHouseKeepingTime
- ? Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: Text("services".tr(),
- style: theme.textTheme.headlineMedium),
- ),
- const SizedBox(height: 16),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16.0),
- child: HousekeepingButton(
- key: ValueKey(state.isHouseKeepingOrdered),
- booking: cubit.booking),
- ),
- ],
- )
- : const SizedBox(),
- const SizedBox(height: 400),
+ // if (cubit.booking.rooms.length > 1)
+ // Padding(
+ // padding: const EdgeInsets.symmetric(horizontal: 16),
+ // child: Text('rooms'.tr(), style: theme.textTheme.headlineMedium),
+ // ),
+ // const SizedBox(height: 16),
+ // if (state.keys.isNotEmpty)
+ // Padding(
+ // padding: const EdgeInsets.symmetric(horizontal: 16),
+ // child:
+ // Text('room_keys'.tr(), style: theme.textTheme.headlineMedium),
+ // ),
+ // const SizedBox(height: 16),
+ const SizedBox(height: 50),
],
);
}
@@ -179,7 +185,7 @@ class BookingDetailsBottomSheet extends StatelessWidget {
onTap: () async {
await context.pushNamed(AppRoutes.upSalesCatalog.name,
extra: [cubit.booking]);
- cubit.add(InitialEvent());
+ cubit.add(const InitialEvent());
},
),
),
diff --git a/comwell_key_app/lib/booking_details/components/check_in_button.dart b/comwell_key_app/lib/booking_details/components/check_in_button.dart
index 966f3416..fe62a22f 100644
--- a/comwell_key_app/lib/booking_details/components/check_in_button.dart
+++ b/comwell_key_app/lib/booking_details/components/check_in_button.dart
@@ -24,9 +24,10 @@ class CheckInButton extends StatelessWidget {
margin: const EdgeInsets.symmetric(horizontal: 10),
child: ElevatedButton(
onPressed: () async {
- await context.pushNamed(AppRoutes.checkIn.name, extra: [bloc.booking, false]);
- if (!bloc.isClosed) {
- bloc.add(InitialEvent());
+ final (result) = await context
+ .pushNamed(AppRoutes.checkIn.name, extra: [bloc.booking, false]);
+ if (result == true && !bloc.isClosed) {
+ bloc.add(CheckInEvent());
}
},
child: Padding(
diff --git a/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart b/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart
index f42f3fe2..9b0ada09 100644
--- a/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart
+++ b/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart
@@ -30,9 +30,9 @@ class _CheckInButtonTimerState extends State<CheckInButtonTimer> {
child: child,
);
},
- child: state.remainingTime.isNegative
- ? const CheckInButton(key: ValueKey('check_in_button'))
- : getTimerWidget());
+ child: const CheckInButton(key: ValueKey('check_in_button'))); // TODO: state.remainingTime.isNegative
+ //? const CheckInButton(key: ValueKey('check_in_button'))
+ //: getTimerWidget());
},
);
}
diff --git a/comwell_key_app/lib/booking_details/components/check_out_button.dart b/comwell_key_app/lib/booking_details/components/check_out_button.dart
index 151a7a25..49563c82 100644
--- a/comwell_key_app/lib/booking_details/components/check_out_button.dart
+++ b/comwell_key_app/lib/booking_details/components/check_out_button.dart
@@ -26,7 +26,7 @@ class CheckOutButton extends StatelessWidget {
onTap: () async {
await context.pushNamed(AppRoutes.checkOut.name,
extra: cubit.booking);
- cubit.add(InitialEvent());
+ cubit.add(const InitialEvent());
},
child: Padding(
padding: const EdgeInsets.all(16.0),
diff --git a/comwell_key_app/lib/booking_details/components/get_keys_button.dart b/comwell_key_app/lib/booking_details/components/get_keys_button.dart
index c9ddbf1d..eddd6d56 100644
--- a/comwell_key_app/lib/booking_details/components/get_keys_button.dart
+++ b/comwell_key_app/lib/booking_details/components/get_keys_button.dart
@@ -19,9 +19,9 @@ class GetKeysButton extends StatelessWidget {
child: ElevatedButton(
onPressed: () async {
const bool onlyKeys = true;
- await context.pushNamed(AppRoutes.checkIn.name, extra: [bloc.booking, onlyKeys],);
- if (!bloc.isClosed) {
- bloc.add(InitialEvent());
+ final (result) = await context.pushNamed(AppRoutes.checkIn.name, extra: [bloc.booking, onlyKeys],);
+ if (result == true && !bloc.isClosed) {
+ bloc.add(CheckInEvent());
}
},
child: Padding(
diff --git a/comwell_key_app/lib/booking_details/components/guest_list.dart b/comwell_key_app/lib/booking_details/components/guest_list.dart
index 868082c8..294eb5bf 100644
--- a/comwell_key_app/lib/booking_details/components/guest_list.dart
+++ b/comwell_key_app/lib/booking_details/components/guest_list.dart
@@ -1,16 +1,23 @@
-import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/booking_details/components/remove_guest_dialog.dart';
+import 'package:comwell_key_app/overview/models/guest.dart';
+import 'package:comwell_key_app/themes/comwell_colors.dart';
+import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class GuestList extends StatelessWidget {
- final Iterable<String> guests;
+ final Iterable<Guest> guests;
final Iterable<String> selectedGuests;
+ final bool isOverview;
final void Function(Iterable<String>) onGuestSelected;
+ final void Function(int guestId) onGuestRemoved;
const GuestList({
super.key,
required this.guests,
required this.selectedGuests,
required this.onGuestSelected,
+ this.isOverview = false,
+ required this.onGuestRemoved,
});
void handleCancelSharing(BuildContext context) {
@@ -20,23 +27,24 @@ class GuestList extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final guestNames =
+ guests.map((guest) => "${guest.firstName} ${guest.lastName}").toList();
return ListView.builder(
shrinkWrap: true,
- itemCount: guests.length,
+ itemCount: guestNames.length,
itemBuilder: (context, index) {
- final guest = guests.elementAt(index);
- final initials = guest.split(' ').map((name) => name[0]).join('');
-
+ final guestName = guestNames.elementAt(index);
+ final initials = guestName.split(' ').map((name) => name[0]).join('');
+ final guestId = guests.elementAt(index).id;
return Padding(
- padding:
- const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
+ padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: GestureDetector(
onTap: () {
final newSelectedGuests = List<String>.from(selectedGuests);
- if (selectedGuests.contains(guest)) {
- newSelectedGuests.remove(guest);
+ if (selectedGuests.contains(guestName)) {
+ newSelectedGuests.remove(guestName);
} else {
- newSelectedGuests.add(guest);
+ newSelectedGuests.add(guestName);
}
onGuestSelected(newSelectedGuests);
},
@@ -72,25 +80,46 @@ class GuestList extends StatelessWidget {
const SizedBox(width: 16),
Expanded(
child: Text(
- guest,
+ guestName,
style: Theme.of(context).textTheme.bodyLarge,
),
),
- Checkbox(
- value: selectedGuests.contains(guest),
- onChanged: (bool? value) {
- if (value != null) {
- final newSelectedGuests =
- List<String>.from(selectedGuests);
- if (value) {
- newSelectedGuests.add(guest);
- } else {
- newSelectedGuests.remove(guest);
- }
- onGuestSelected(newSelectedGuests);
- }
- },
- ),
+ !isOverview
+ ? Checkbox(
+ value: selectedGuests.contains(guestName),
+ onChanged: (bool? value) {
+ if (value != null) {
+ final newSelectedGuests =
+ List<String>.from(selectedGuests);
+ if (value) {
+ newSelectedGuests.add(guestName);
+ } else {
+ newSelectedGuests.remove(guestName);
+ }
+ onGuestSelected(newSelectedGuests);
+ }
+ },
+ )
+ : TextButton(
+ onPressed: () async {
+ final shouldRemove = await showDialog<bool>(
+ context: context,
+ builder: (context) => RemoveGuestDialog(
+ selectedGuests: selectedGuests,
+ onGuestSelected: onGuestSelected));
+ if (shouldRemove == true && context.mounted) {
+ onGuestRemoved(guestId);
+ Navigator.pop(context);
+ }
+ },
+ child: Text('remove_guest_overview'.tr(),
+ style: Theme.of(context)
+ .textTheme
+ .bodyLarge
+ ?.copyWith(
+ color: colorError,
+ fontWeight: FontWeight.w600)),
+ ),
],
),
),
diff --git a/comwell_key_app/lib/booking_details/components/preregister_button.dart b/comwell_key_app/lib/booking_details/components/preregister_button.dart
index d5d6b8af..03237e5b 100644
--- a/comwell_key_app/lib/booking_details/components/preregister_button.dart
+++ b/comwell_key_app/lib/booking_details/components/preregister_button.dart
@@ -16,6 +16,8 @@ class PreregisterButton extends StatelessWidget {
final bloc = context.read<BookingDetailsBloc>();
final theme = Theme.of(context);
+
+
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8),
child: ElevatedButton(
@@ -25,21 +27,6 @@ class PreregisterButton extends StatelessWidget {
extra: [bloc.booking, bloc.state.upSales]);
if (result != null) {
bloc.add(PreregisterEvent());
- } else {
- if (!context.mounted) return;
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(
- "error_preregistration".tr(),
- style: theme.textTheme.bodyMedium,
- textAlign: TextAlign.center,
- ),
- backgroundColor: theme.scaffoldBackgroundColor,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- ),
- );
}
},
child: Padding(
diff --git a/comwell_key_app/lib/booking_details/components/remove_guest_dialog.dart b/comwell_key_app/lib/booking_details/components/remove_guest_dialog.dart
new file mode 100644
index 00000000..f60a937d
--- /dev/null
+++ b/comwell_key_app/lib/booking_details/components/remove_guest_dialog.dart
@@ -0,0 +1,93 @@
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class RemoveGuestDialog extends StatelessWidget {
+ final Iterable<String> selectedGuests;
+ final void Function(Iterable<String>) onGuestSelected;
+
+ const RemoveGuestDialog(
+ {super.key, required this.selectedGuests, required this.onGuestSelected});
+
+ @override
+ Widget build(BuildContext context) {
+ return AlertDialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8.0),
+ ),
+ contentPadding: const EdgeInsets.all(24.0),
+ backgroundColor: colorBackground,
+ title: Center(
+ child: Text(
+ 'are_you_sure'.tr(),
+ style: const TextStyle(
+ fontSize: 20,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'guest_removal_responsibility'.tr(),
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 16,
+ color: Colors.grey[500],
+ ),
+ ),
+ const SizedBox(height: 16),
+ ElevatedButton(
+ onPressed: () {
+ Navigator.pop(context, true);
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: colorBackground,
+ side: BorderSide(color: Colors.grey[300]!, width: 1),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(25),
+ ),
+ elevation: 0,
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ minimumSize: const Size.fromHeight(50),
+ ),
+ child: Text(
+ selectedGuests.length > 1
+ ? 'remove_guests'.tr()
+ : 'remove_guest'.tr(),
+ style: const TextStyle(
+ color: colorTertiary,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ const SizedBox(height: 12),
+ ElevatedButton(
+ onPressed: () {
+ Navigator.pop(context, false);
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: sandColor,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(25),
+ ),
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ minimumSize: const Size.fromHeight(50),
+ ),
+ child: Text(
+ 'cancel'.tr(),
+ style: const TextStyle(
+ color: colorBackground,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ],
+ ),
+ actions: const [],
+ );
+ }
+}
diff --git a/comwell_key_app/lib/booking_details/components/share_button.dart b/comwell_key_app/lib/booking_details/components/share_button.dart
index 01ab43b2..5e5b0eb2 100644
--- a/comwell_key_app/lib/booking_details/components/share_button.dart
+++ b/comwell_key_app/lib/booking_details/components/share_button.dart
@@ -16,12 +16,14 @@ class ShareButton extends StatelessWidget {
final Color? buttonColor;
final double userButtonSize;
final double userButtonOverlap;
+ final bool isMyBooking;
const ShareButton(
{super.key,
required this.guests,
this.buttonColor,
this.userButtonSize = 45,
- this.userButtonOverlap = 10});
+ this.userButtonOverlap = 10,
+ this.isMyBooking = false});
@override
Widget build(BuildContext context) {
@@ -37,7 +39,8 @@ class ShareButton extends StatelessWidget {
Align(
alignment: Alignment.topLeft,
child: Padding(
- padding: const EdgeInsets.only(left: 16.0, top: 16),
+ padding: EdgeInsets.only(
+ left: isMyBooking ? 40 : 16.0, top: isMyBooking ? 24 : 16),
child: Stack(
children: [
Positioned(
@@ -60,8 +63,9 @@ class ShareButton extends StatelessWidget {
padding: EdgeInsets.zero,
),
child: Center(
- child:
- Icon(Icons.add, color: colorHeadlineText, size: 30),
+ child: Icon(Icons.add,
+ color: colorHeadlineText,
+ size: isMyBooking ? 20 : 30),
),
),
),
@@ -76,8 +80,14 @@ class ShareButton extends StatelessWidget {
height: userButtonSize,
child: ElevatedButton(
onPressed: () async {
+ if (isMyBooking) {
+ return;
+ }
final results = await _showGuestList(
- context, index, guests, booking.booker.name);
+ context,
+ index,
+ guests,
+ "${booking.firstName} ${booking.lastName}");
if (results is Iterable<String>) {
final updatedBooking =
@@ -95,10 +105,10 @@ class ShareButton extends StatelessWidget {
child: Center(
child: Text(
allInitials.elementAt(index),
- style: const TextStyle(
+ style: TextStyle(
color: colorBackground,
fontWeight: FontWeight.w400,
- fontSize: 18,
+ fontSize: isMyBooking ? 14 : 18,
),
),
),
@@ -117,16 +127,10 @@ class ShareButton extends StatelessWidget {
Future<dynamic> _showGuestList(BuildContext context, int index,
Iterable<Guest> guests, String booker) async {
final theme = Theme.of(context);
-
+ final selectedGuests = guests.map((e) => e.id.toString()).toList();
return showModalBottomSheet<dynamic>(
context: context,
backgroundColor: colorBackground,
- shape: const RoundedRectangleBorder(
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16),
- ),
- ),
isScrollControlled: false,
builder: (BuildContext bottomSheetContext) {
return BlocProvider(
@@ -135,10 +139,6 @@ class ShareButton extends StatelessWidget {
listener: (context, state) {},
builder: (context, state) {
final cubit = context.read<ShareBookingCubit>();
- final guestsWithoutBooker = guests
- .where((guest) => guest.name != booker)
- .map((guest) => guest.name)
- .toList();
return ClipRRect(
borderRadius: const BorderRadius.only(
@@ -190,10 +190,17 @@ class ShareButton extends StatelessWidget {
}
return Expanded(
child: GuestList(
- guests: guestsWithoutBooker,
- selectedGuests: state.selectedGuests,
+ guests: guests,
+ selectedGuests: selectedGuests,
onGuestSelected: (Iterable<String> newSelection) {
- cubit.updateSelectedGuests(newSelection);
+ cubit.updateSelectedGuests(newSelection.map(
+ (e) => Guest(
+ id: int.parse(e),
+ firstName: e.split(' ')[0],
+ lastName: e.split(' ')[1])));
+ },
+ onGuestRemoved: (int guestId) {
+ cubit.removeGuests([guestId]);
},
));
})
@@ -224,7 +231,7 @@ class ShareButton extends StatelessWidget {
context,
cubit,
bottomSheetContext,
- state.selectedGuests,
+ selectedGuests,
),
);
diff --git a/comwell_key_app/lib/booking_details/components/unlock_room_button.dart b/comwell_key_app/lib/booking_details/components/unlock_room_button.dart
index 94b1f61e..da626451 100644
--- a/comwell_key_app/lib/booking_details/components/unlock_room_button.dart
+++ b/comwell_key_app/lib/booking_details/components/unlock_room_button.dart
@@ -8,7 +8,9 @@ import '../../routing/app_routes.dart';
import '../../themes/dark_theme.dart';
class UnlockRoomButton extends StatelessWidget {
- const UnlockRoomButton({super.key});
+ final String roomNumber;
+
+ const UnlockRoomButton({super.key, required this.roomNumber});
@override
Widget build(BuildContext context) {
@@ -27,7 +29,7 @@ class UnlockRoomButton extends StatelessWidget {
),
),
action: () async {
- context.pushNamed(AppRoutes.key.name);
+ context.pushNamed(AppRoutes.key.name, extra: roomNumber);
return false;
},
alignLabel: Alignment.center,
diff --git a/comwell_key_app/lib/check_in/bloc/check_in_cubit.dart b/comwell_key_app/lib/check_in/bloc/check_in_cubit.dart
index eaf5d502..1da306f8 100644
--- a/comwell_key_app/lib/check_in/bloc/check_in_cubit.dart
+++ b/comwell_key_app/lib/check_in/bloc/check_in_cubit.dart
@@ -25,9 +25,9 @@ class CheckInCubit extends Cubit<CheckInState> {
debugPrint("bookingDetails ${bookingDetails.roomNumber}");
await _checkInRepository.checkIfSetup();
debugPrint("checkIfSetup");
+ await _checkInRepository.provisionKey(booking.id, booking.hotelCode);
+ await Future<void>.delayed(const Duration(milliseconds: 3000));
await tryGetKeys();
-
- await Future<void>.delayed(const Duration(milliseconds: 1000));
emit(state.checkInStatusYourDigitalCard(
roomNumber: bookingDetails.roomNumber));
debugPrint("checkInStatusYourDigitalCard");
@@ -41,11 +41,13 @@ class CheckInCubit extends Cubit<CheckInState> {
try {
await Future<void>.delayed(const Duration(milliseconds: 500));
emit(state.checkInStatusLoading());
- await _checkInRepository.checkIn(booking.confirmationId);
+ await _checkInRepository.checkIn(booking.confirmationNumber);
final bookingDetails =
await _checkInRepository.getBookingDetails(booking.id, booking.hotelCode);
emit(state.checkInStatusRoomFound(roomNumber: bookingDetails.roomNumber));
await _checkInRepository.checkIfSetup();
+ await _checkInRepository.provisionKey(booking.id, booking.hotelCode);
+ await Future<void>.delayed(const Duration(milliseconds: 3000));
await tryGetKeys();
await Future<void>.delayed(const Duration(milliseconds: 1000));
@@ -70,14 +72,13 @@ class CheckInCubit extends Cubit<CheckInState> {
Future<void> tryGetKeys({int attempt = 0}) async {
try {
- await _checkInRepository.getKeys(booking.id);
+ await _checkInRepository.getKeys(booking.id, booking.hotelCode);
} catch (e) {
if (attempt < getKeysRetryAttempts) {
await Future<void>.delayed(const Duration(milliseconds: 500));
tryGetKeys(attempt: attempt + 1);
} else {
emit(state.checkInStatusError(Exception(e.toString())));
- rethrow;
}
}
}
diff --git a/comwell_key_app/lib/check_in/check_in_page.dart b/comwell_key_app/lib/check_in/check_in_page.dart
index 644900d1..6623d078 100644
--- a/comwell_key_app/lib/check_in/check_in_page.dart
+++ b/comwell_key_app/lib/check_in/check_in_page.dart
@@ -150,6 +150,16 @@ class _CheckInPageState extends State<CheckInPage>
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 15),
+ Text("check_in_page_error_title".tr(),
+ textAlign: TextAlign.center,
+ style: theme.textTheme.headlineMedium
+ ?.copyWith(color: colorHeadlineText)),
+ const SizedBox(height: 15),
+ Text("check_in_page_error_subtitle".tr(),
+ textAlign: TextAlign.center,
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+ const SizedBox(height: 15),
Text(state.subtitleStringId,
style: theme.textTheme.bodySmall),
const SizedBox(height: 15),
@@ -167,9 +177,11 @@ class _CheckInPageState extends State<CheckInPage>
context.pop();
},
style: ButtonStyle(
- backgroundColor: WidgetStatePropertyAll(sandColor[80]),
+ backgroundColor:
+ WidgetStatePropertyAll(sandColor[80]),
foregroundColor:
- const WidgetStatePropertyAll(colorBackground)),
+ const WidgetStatePropertyAll(
+ colorBackground)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0),
@@ -193,7 +205,8 @@ class _CheckInPageState extends State<CheckInPage>
padding: const EdgeInsets.all(16.0),
child: Text("check_in_your_digital_card_nb".tr(),
textAlign: TextAlign.center,
- style: theme.textTheme.bodySmall?.copyWith(color: colorHeadlineText)),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
),
const SizedBox(height: 15),
Padding(
@@ -214,13 +227,14 @@ class _CheckInPageState extends State<CheckInPage>
child: ElevatedButton(
onPressed: () {
cubit.onDonePressed();
- context.pop();
+ context.pop(true);
},
style: ButtonStyle(
- backgroundColor: WidgetStatePropertyAll(
- sandColor[80]),
+ backgroundColor:
+ WidgetStatePropertyAll(sandColor[80]),
foregroundColor:
- const WidgetStatePropertyAll(colorBackground)),
+ const WidgetStatePropertyAll(
+ colorBackground)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0),
diff --git a/comwell_key_app/lib/check_in/check_in_repository.dart b/comwell_key_app/lib/check_in/check_in_repository.dart
index 8490db30..57a13ea3 100644
--- a/comwell_key_app/lib/check_in/check_in_repository.dart
+++ b/comwell_key_app/lib/check_in/check_in_repository.dart
@@ -1,6 +1,6 @@
+import 'package:comwell_key_app/booking_details/booking_details_repository.dart';
import 'package:comwell_key_app/database/comwell_db.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/overview/models/guest.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/utils/locator.dart';
@@ -14,10 +14,11 @@ class CheckInRepository {
final db = locator.get<ComwellDatabase>();
final seosRepository = locator<SeosRepository>();
final profileRepository = locator<ProfileRepository>();
+ final bookingDetailsRepository = locator<BookingDetailsRepository>();
- Future<dynamic> checkIn(String confirmationId) async {
+ Future<dynamic> checkIn(String confirmationNumber) async {
try {
- final response = await api.checkIn(confirmationId);
+ final response = await api.checkIn(confirmationNumber);
return response;
} on DioException catch (err) {
if (err.response?.data != null) {
@@ -32,7 +33,7 @@ class CheckInRepository {
}
Future<Booking> getBookingDetails(String hmsConfirmationNumber, String hotelCode) async {
- final booking = await profileRepository.getBookingDetails(hmsConfirmationNumber, hotelCode);
+ final booking = await bookingDetailsRepository.getBookingDetails(hmsConfirmationNumber, hotelCode);
return booking;
}
@@ -41,40 +42,41 @@ class CheckInRepository {
if (!isSetup) await seosRepository.startMobilePlugin();
}
- Future<void> getKeys(String hmsConfirmationNumber) async {
- final keys = await seosRepository.refreshKeys();
-
- if (keys.isEmpty) {
- await seosRepository.provisionKey(bookingId: hmsConfirmationNumber);
+ Future<void> provisionKey(String hmsConfirmationNumber, String hotelCode) async {
+ await seosRepository.provisionKey(bookingId: hmsConfirmationNumber, hotelCode: hotelCode);
+ }
+ Future<void> getKeys(String hmsConfirmationNumber, String hotelCode) async {
final keys = await seosRepository.refreshKeys();
+ debugPrint("keys: $keys");
if (keys.isEmpty) {
throw Exception("Could not provision key for this booking");
}
- }
+
}
static final mockBooking = Booking(
id: "helloworld",
+ firstName: "firstName",
+ lastName: "lastName",
+ bookerFirstName: "bookerFirstName",
+ bookerLastName: "bookerLastName",
+ isPrimaryGuest: false,
roomNumber: "1234",
startDate: DateTime.now(),
endDate: DateTime.now(),
- hmsConfirmationNumber: "hmsConfirmationNumber",
+ confirmationNumber: "confirmationNumber",
reservationStatus: ReservationStatus.newreservation,
image: "",
hotelName: "hotelName",
roomType: "roomType",
- confirmationId: "",
children: 0,
adults: 2,
hotelCode: "hotelCode",
- totalCharge: 100,
- balance: 0,
+ balance: 100,
maskedCardNumber: "1234567890",
- booker: const Guest(name: "Booker", id: "123"),
bookingDate: DateTime.now(),
digitalCard: true,
guests: null,
- rooms: const [],
addOnItems: const [],
);
}
diff --git a/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart b/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart
index 02f36b7e..11fc2647 100644
--- a/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart
+++ b/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart
@@ -1,5 +1,6 @@
import 'package:adyen_checkout/adyen_checkout.dart';
import 'package:bloc/bloc.dart';
+import 'package:comwell_key_app/booking_details/booking_details_repository.dart';
import 'package:comwell_key_app/check_out/bloc/check_out_state.dart';
import 'package:comwell_key_app/check_out/check_out_repository.dart';
import 'package:comwell_key_app/check_out/pages/check_out_page.dart';
@@ -21,6 +22,7 @@ class CheckoutCubit extends Cubit<CheckoutState> {
final ProfileRepository profileRepository = locator<ProfileRepository>();
final PreregistrationRepository preregistrationRepository =
locator<PreregistrationRepository>();
+ final BookingDetailsRepository bookingDetailsRepository = locator<BookingDetailsRepository>();
final CheckOutRepository checkOutRepository;
final _tracking = locator<ComwellTracking>();
final pageController = PageController();
@@ -38,9 +40,12 @@ class CheckoutCubit extends Cubit<CheckoutState> {
void init() async {
try {
//TODO: Fetch club points eventually
+ emit(state.loading());
final user = await profileRepository.fetchProfileSettings();
- //emit(state.clubPointsFetched(user.points));
+ emit(state.clubPointsFetched(user.points));
+ booking =await bookingDetailsRepository.getRemoteBookingDetails(booking.confirmationNumber, booking.hotelCode);
setItems(booking.addOnItems ?? []);
+ emit(state.loaded());
} catch (e) {
emit(state.checkoutError());
}
@@ -72,7 +77,7 @@ class CheckoutCubit extends Cubit<CheckoutState> {
_tracking.trackBeginCheckout(analyticsEventItem);
try {
await paymentServicesCubit.createSession(booking.balance?.toInt() ?? 0,
- booking.confirmationId, state.applyClubPoints);
+ booking.confirmationNumber, state.applyClubPoints, booking.hotelCode);
await Future<void>.delayed(const Duration(milliseconds: 4000));
} catch (e) {
emit(state.checkoutError());
@@ -92,10 +97,10 @@ class CheckoutCubit extends Cubit<CheckoutState> {
_isAnimating = true;
switch (currentPage) {
case CheckoutPage.confirmation:
- if (booking.balance == 0) {
- emit(state.paymentProcessingNotNeeded());
- return _navigateToProcessing(context);
- }
+ // if (booking.balance == 0 || booking.balance == null) {
+ // emit(state.paymentProcessingNotNeeded());
+ // return _navigateToProcessing(context);
+ // }
return _navigateTo(CheckoutPage.payment);
case CheckoutPage.payment:
return _navigateToProcessing(context);
@@ -107,7 +112,7 @@ class CheckoutCubit extends Cubit<CheckoutState> {
if (!state.isTermsAccepted && (booking.balance ?? 0.0) > 0.0) {
emit(state.showAcceptTermsError());
} else {
- if (booking.balance == 0) {
+ if (booking.balance == 0 || booking.balance == null) {
processCheckoutWithoutPaying();
} else {
context.pushNamed(AppRoutes.paymentProcessing.name);
@@ -156,10 +161,10 @@ class CheckoutCubit extends Cubit<CheckoutState> {
Future<void> checkOut() async {
try {
booking =
- await profileRepository.getBookingDetails(booking.confirmationId, booking.hotelCode);
+ await bookingDetailsRepository.getRemoteBookingDetails(booking.confirmationNumber, booking.hotelCode);
- if (booking.balance == 0) {
- await checkOutRepository.checkOut(booking.confirmationId);
+ if (booking.balance == 0 || booking.balance == null) {
+ await checkOutRepository.checkOut(booking.confirmationNumber);
emit(state.checkoutSuccess());
} else {
diff --git a/comwell_key_app/lib/check_out/bloc/check_out_state.dart b/comwell_key_app/lib/check_out/bloc/check_out_state.dart
index 8fb5f812..efb1124c 100644
--- a/comwell_key_app/lib/check_out/bloc/check_out_state.dart
+++ b/comwell_key_app/lib/check_out/bloc/check_out_state.dart
@@ -16,6 +16,7 @@ class CheckoutState extends Equatable {
final bool showTermsError;
final num bookingBalance;
final bool isPaymentProcessingNeeded;
+ final bool isLoading;
int get totalPriceBeforeDiscount => _sumOfList(_items);
@@ -50,6 +51,7 @@ class CheckoutState extends Equatable {
required this.bookingBalance,
required this.successfulCheckout,
required this.isPaymentProcessingNeeded,
+ required this.isLoading,
}) : _items = items;
CheckoutState.initial(this.bookingBalance)
@@ -57,10 +59,11 @@ class CheckoutState extends Equatable {
isTermsAccepted = false,
page = CheckoutPage.confirmation,
showTermsError = false,
- clubPoints = 500,
+ clubPoints = 0,
paymentMethod = CheckoutPaymentMethod.creditCard,
applyClubPoints = false,
successfulCheckout = false,
+ isLoading = false,
isPaymentProcessingNeeded = true;
CheckoutState itemsUpdated(Iterable<BookingAddonItem> items) {
@@ -72,6 +75,8 @@ class CheckoutState extends Equatable {
return _copyWith(items: newItems);
}
+ CheckoutState loading() => _copyWith(isLoading: true);
+ CheckoutState loaded() => _copyWith(isLoading: false);
CheckoutState termsAccepted() => _copyWith(termsAccepted: true, showTermsError: false);
CheckoutState termsDenied() => _copyWith(termsAccepted: false);
@@ -110,6 +115,7 @@ class CheckoutState extends Equatable {
num? bookingBalance,
bool? successfulCheckout,
bool? isPaymentProcessingNeeded,
+ bool? isLoading,
}) {
return CheckoutState(
items: items ?? _items,
@@ -121,7 +127,8 @@ class CheckoutState extends Equatable {
paymentMethod: paymentMethod ?? this.paymentMethod,
bookingBalance: bookingBalance ?? this.bookingBalance,
successfulCheckout: successfulCheckout ?? this.successfulCheckout,
- isPaymentProcessingNeeded: isPaymentProcessingNeeded ?? this.isPaymentProcessingNeeded);
+ isPaymentProcessingNeeded: isPaymentProcessingNeeded ?? this.isPaymentProcessingNeeded,
+ isLoading: isLoading ?? this.isLoading);
}
@override
@@ -138,6 +145,7 @@ class CheckoutState extends Equatable {
totalPrice,
successfulCheckout,
isPaymentProcessingNeeded,
+ isLoading,
];
}
diff --git a/comwell_key_app/lib/check_out/check_out_flow.dart b/comwell_key_app/lib/check_out/check_out_flow.dart
index 93f1814d..04118467 100644
--- a/comwell_key_app/lib/check_out/check_out_flow.dart
+++ b/comwell_key_app/lib/check_out/check_out_flow.dart
@@ -23,7 +23,7 @@ class CheckOutFlow extends StatelessWidget {
listener: (context, state) async {
if (state is PaymentProcessingStateConfirmed) {
//This is here to add time so that the payment is represented in the BookingDetails
- await Future<void>.delayed(const Duration(seconds: 5));
+ await Future<void>.delayed(const Duration(seconds: 1));
await cubit.checkOut();
if (cubit.state.successfulCheckout && context.mounted) {
context.pushNamed(AppRoutes.checkOutSuccess.name,
@@ -41,7 +41,7 @@ class CheckOutFlow extends StatelessWidget {
if (!didScroll) context.pop();
},
),
- bottomSheet: const CheckOutBottomSheet(),
+ bottomSheet: CheckOutBottomSheet(state: cubit.state),
backgroundColor: Colors.white,
body: PageView(
controller: cubit.pageController,
diff --git a/comwell_key_app/lib/check_out/components/accept_terms_toggle.dart b/comwell_key_app/lib/check_out/components/accept_terms_toggle.dart
index 8e382da1..15130b62 100644
--- a/comwell_key_app/lib/check_out/components/accept_terms_toggle.dart
+++ b/comwell_key_app/lib/check_out/components/accept_terms_toggle.dart
@@ -1,4 +1,5 @@
import 'package:comwell_key_app/themes/dark_theme.dart';
+import 'package:comwell_key_app/themes/light_theme.dart' show colorDivider;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@@ -27,7 +28,11 @@ class AcceptTermsToggle extends StatelessWidget {
children: [
Checkbox(
value: isTermsAccepted,
- visualDensity: VisualDensity.comfortable,
+ visualDensity: VisualDensity.compact,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(4),
+ ),
+ side: const BorderSide(color: colorDivider, width: 2,),
checkColor: Colors.white,
activeColor: sandColor[80],
onChanged: (value) {
diff --git a/comwell_key_app/lib/check_out/components/apply_club_points.dart b/comwell_key_app/lib/check_out/components/apply_club_points.dart
index e4249f3c..fa15d5b4 100644
--- a/comwell_key_app/lib/check_out/components/apply_club_points.dart
+++ b/comwell_key_app/lib/check_out/components/apply_club_points.dart
@@ -14,6 +14,7 @@ class ApplyClubPoints extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final theme = Theme.of(context);
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -29,7 +30,8 @@ class ApplyClubPoints extends StatelessWidget {
args: ["$clubPoints", "$clubPoints"],
)
: "checkout_page_payment_club_points_subtitle_zero".tr(),
- style: TextStyle(color: colorBlack[65]),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorBlack[65]),
),
],
),
@@ -38,9 +40,18 @@ class ApplyClubPoints extends StatelessWidget {
if (clubPoints > 0)
Switch(
value: applyClubPoints,
- activeThumbColor: sandColor[80],
- inactiveTrackColor: Colors.grey[200],
- inactiveThumbColor: Colors.white,
+ thumbColor: WidgetStateProperty.resolveWith((states) {
+ if (states.contains(WidgetState.selected)) {
+ return sandColor[80];
+ }
+ return Colors.white;
+ }),
+ trackColor: WidgetStateProperty.resolveWith((states) {
+ if (!states.contains(WidgetState.selected)) {
+ return Colors.grey[200];
+ }
+ return null;
+ }),
trackOutlineColor: const WidgetStatePropertyAll(Colors.white),
onChanged: (value) {
onApplyClubPointsChanged(value);
diff --git a/comwell_key_app/lib/check_out/components/check_out_bottom_sheet.dart b/comwell_key_app/lib/check_out/components/check_out_bottom_sheet.dart
index 102327d1..41280c2b 100644
--- a/comwell_key_app/lib/check_out/components/check_out_bottom_sheet.dart
+++ b/comwell_key_app/lib/check_out/components/check_out_bottom_sheet.dart
@@ -1,7 +1,5 @@
-import 'dart:io';
-
import 'package:comwell_key_app/check_out/bloc/check_out_cubit.dart';
-import 'package:comwell_key_app/check_out/models/payment_method.dart';
+import 'package:comwell_key_app/check_out/bloc/check_out_state.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@@ -12,13 +10,25 @@ import '../pages/check_out_page.dart';
import 'confirm_check_out_dialog.dart';
class CheckOutBottomSheet extends StatelessWidget {
- const CheckOutBottomSheet({super.key});
+ final CheckoutState state;
+ const CheckOutBottomSheet({super.key, required this.state});
@override
Widget build(BuildContext context) {
final cubit = context.read<CheckoutCubit>();
- final paymentMethod = cubit.state.paymentMethod!;
final theme = Theme.of(context);
+ final isEnabled = state.isTermsAccepted;
+
+ final buttonStyle = ButtonStyle(
+ backgroundColor: WidgetStateProperty.resolveWith((states) {
+ if (states.contains(WidgetState.disabled)) {
+ return sandColor.withOpacity(0.5);
+ }
+ return sandColor;
+ }),
+ foregroundColor: const WidgetStatePropertyAll(colorBackground),
+ );
+
return Container(
decoration:
const BoxDecoration(shape: BoxShape.rectangle, color: colorBackground),
@@ -35,23 +45,20 @@ class CheckOutBottomSheet extends StatelessWidget {
child: Builder(builder: (context) {
if (cubit.state.page == CheckoutPage.confirmation) {
return ElevatedButton(
- onPressed: cubit.booking.balance == 0
- ? () async {
- await showDialog<void>(
- context: context,
- builder: (context) =>
- ConfirmCheckOutDialog(onConfirm: () {
- context.pop();
- cubit.onContinueClicked(context);
- }),
- );
- }
- : () => cubit.onContinueClicked(context),
- style: ButtonStyle(
- backgroundColor:
- WidgetStatePropertyAll(sandColor[100]),
- foregroundColor:
- const WidgetStatePropertyAll(colorBackground)),
+ onPressed: (cubit.booking.balance != 0
+ ? () async {
+ await showDialog<void>(
+ context: context,
+ builder: (context) =>
+ ConfirmCheckOutDialog(onConfirm: () {
+ context.pop();
+ cubit.onContinueClicked(context);
+ }),
+ );
+ }
+ : () => cubit.onContinueClicked(context))
+ ,
+ style: buttonStyle,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 17.0),
child: Text(
@@ -63,34 +70,20 @@ class CheckOutBottomSheet extends StatelessWidget {
),
));
} else {
- return OutlinedButton(
- onPressed: () async {
- await showDialog<void>(
- context: context,
- builder: (context) =>
- ConfirmCheckOutDialog(onConfirm: () {
- Navigator.of(context).pop();
- cubit.onContinueClicked(context);
- }),
- );
- },
- style: ButtonStyle(
- backgroundColor: paymentMethod ==
- CheckoutPaymentMethod
- .appleOrGooglePay &&
- Platform.isIOS
- ? const WidgetStatePropertyAll(colorTertiary)
- : Platform.isAndroid &&
- paymentMethod ==
- CheckoutPaymentMethod
- .appleOrGooglePay
- ? const WidgetStatePropertyAll(
- colorBackground)
- : const WidgetStatePropertyAll(sandColor),
- foregroundColor:
- const WidgetStatePropertyAll(colorBackground),
- side: const WidgetStatePropertyAll(
- BorderSide(color: colorDivider))),
+ return ElevatedButton(
+ onPressed: isEnabled
+ ? () async {
+ await showDialog<void>(
+ context: context,
+ builder: (context) =>
+ ConfirmCheckOutDialog(onConfirm: () {
+ Navigator.of(context).pop();
+ cubit.onContinueClicked(context);
+ }),
+ );
+ }
+ : null,
+ style: buttonStyle,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 17.0),
child: Text(
diff --git a/comwell_key_app/lib/check_out/components/send_receipt.dart b/comwell_key_app/lib/check_out/components/send_receipt.dart
new file mode 100644
index 00000000..20c1dce8
--- /dev/null
+++ b/comwell_key_app/lib/check_out/components/send_receipt.dart
@@ -0,0 +1,100 @@
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class SendReceipt extends StatelessWidget {
+ final void Function(bool) onSendReceiptChanged;
+ final bool sendReceipt;
+ final String userEmail;
+ const SendReceipt(
+ {super.key,
+ required this.onSendReceiptChanged,
+ required this.sendReceipt,
+ required this.userEmail});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Column(
+ children: [
+ Row(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("payment_overview_send_receipt_title".tr()),
+ Text(
+ "payment_overview_send_receipt_subtitle"
+ .tr(args: [userEmail]),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(width: 10),
+ if (sendReceipt)
+ Switch(
+ value: sendReceipt,
+ thumbColor: WidgetStateProperty.resolveWith((states) {
+ if (states.contains(WidgetState.selected)) {
+ return sandColor[80];
+ }
+ return Colors.white;
+ }),
+ trackColor: WidgetStateProperty.resolveWith((states) {
+ if (!states.contains(WidgetState.selected)) {
+ return Colors.grey[200];
+ }
+ return null;
+ }),
+ trackOutlineColor: const WidgetStatePropertyAll(Colors.white),
+ onChanged: (value) {
+ onSendReceiptChanged(value);
+ },
+ )
+ ],
+ ),
+ const SizedBox(height: 20),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "payment_overview_send_comment_title".tr(),
+ style: theme.textTheme.bodySmall,
+ ),
+ const SizedBox(height: 8),
+ TextField(
+ controller: TextEditingController(),
+ onChanged: (value) {
+ // TODO: Implement send receipt
+ },
+ maxLines: 5,
+ minLines: 3,
+ decoration: InputDecoration(
+ hintText: "payment_overview_send_receipt_hint".tr(),
+ hintStyle: theme.textTheme.headlineSmall?.copyWith(
+ color: colorTertiary,
+ ),
+ enabledBorder: OutlineInputBorder(
+ borderSide: const BorderSide(color: colorDivider),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderSide: const BorderSide(color: colorDivider),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ contentPadding:
+ const EdgeInsets.symmetric(vertical: 20, horizontal: 12),
+ ),
+ style: theme.textTheme.headlineSmall,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/comwell_key_app/lib/check_out/pages/checkout_payment_page.dart b/comwell_key_app/lib/check_out/pages/checkout_payment_page.dart
index cc040992..649e119f 100644
--- a/comwell_key_app/lib/check_out/pages/checkout_payment_page.dart
+++ b/comwell_key_app/lib/check_out/pages/checkout_payment_page.dart
@@ -29,6 +29,7 @@ class CheckoutPaymentPage extends StatelessWidget {
trimmedBalance: trimmedBalance,
clubPoints: clubPoints,
isTermsAccepted: isTermsAccepted,
+ userEmail: cubit.booking.bookerLastName,
onAcceptTermsChanged: onAcceptTermsChanged,
showError: showError,
onShowTermsAndConditions: onShowTermsAndConditions,
diff --git a/comwell_key_app/lib/choose_share_room/choose_share_room_repository.dart b/comwell_key_app/lib/choose_share_room/choose_share_room_repository.dart
index 26d88e57..d2c4ee06 100644
--- a/comwell_key_app/lib/choose_share_room/choose_share_room_repository.dart
+++ b/comwell_key_app/lib/choose_share_room/choose_share_room_repository.dart
@@ -1,9 +1,11 @@
import 'package:comwell_key_app/overview/models/room.dart';
+import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/services/mappers/room_mapper.dart';
import 'package:comwell_key_app/services/models/room_dto.dart';
class ChooseShareRoomRepository {
String baseUrl;
+ final api = Api();
ChooseShareRoomRepository({this.baseUrl = ''});
diff --git a/comwell_key_app/lib/choose_share_room/components/choose_room_widget.dart b/comwell_key_app/lib/choose_share_room/components/choose_room_widget.dart
index 321e38d3..d2c51788 100644
--- a/comwell_key_app/lib/choose_share_room/components/choose_room_widget.dart
+++ b/comwell_key_app/lib/choose_share_room/components/choose_room_widget.dart
@@ -35,7 +35,7 @@ class ChooseRoomWidget extends StatelessWidget {
builder: (context, state) {
final cubit = context.read<ChooseShareRoomCubit>();
final isAssigned = cubit.rooms.any((room) =>
- room.confirmationNumber == booking.rooms.first.confirmationNumber &&
+ room.confirmationNumber == booking.confirmationNumber &&
room.assignedTo != null);
return GestureDetector(
onTap: () async {
@@ -43,7 +43,7 @@ class ChooseRoomWidget extends StatelessWidget {
final result = await context.pushNamed(AppRoutes.roomInfo.name, extra: [booking, isAssigned]);
if (result != null && result is bool) {
await cubit.assignRoomToUser(
- booking.rooms.first.confirmationNumber, booking.booker.id);
+ booking.confirmationNumber, booking.firstName);
}
},
@@ -165,8 +165,8 @@ class ChooseRoomWidget extends StatelessWidget {
);
if (result != null && result) {
await cubit.assignRoomToUser(
- booking.rooms.first.confirmationNumber,
- booking.booker.id);
+ booking.confirmationNumber,
+ booking.firstName);
if (context.mounted) {
context.pop();
}
diff --git a/comwell_key_app/lib/choose_share_room/cubit/choose_share_room_cubit.dart b/comwell_key_app/lib/choose_share_room/cubit/choose_share_room_cubit.dart
index e49f7a67..e5081e4a 100644
--- a/comwell_key_app/lib/choose_share_room/cubit/choose_share_room_cubit.dart
+++ b/comwell_key_app/lib/choose_share_room/cubit/choose_share_room_cubit.dart
@@ -19,6 +19,7 @@ class ChooseShareRoomCubit extends Cubit<ChooseShareRoomState> {
}
Future<void> shareRoom(Booking booking) async {
+
Share.share(
'${booking.hotelName}\n\n'
'${'dates'.tr()}: ${DateFormat('d. MMM').format(booking.startDate)} - ${DateFormat('d. MMM').format(booking.endDate)}\n'
diff --git a/comwell_key_app/lib/choose_share_room/pages/room_info_page.dart b/comwell_key_app/lib/choose_share_room/pages/room_info_page.dart
index 3128c850..c8992ac3 100644
--- a/comwell_key_app/lib/choose_share_room/pages/room_info_page.dart
+++ b/comwell_key_app/lib/choose_share_room/pages/room_info_page.dart
@@ -3,9 +3,9 @@ import 'package:comwell_key_app/choose_share_room/cubit/choose_share_room_cubit.
import 'package:comwell_key_app/choose_share_room/cubit/choose_share_room_state.dart';
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
import 'package:comwell_key_app/common/const.dart';
+import 'package:comwell_key_app/overview/models/room.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:comwell_key_app/common/components/room_image_carousel.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/up_sales/models/facility_type.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -31,14 +31,11 @@ class _RoomInfoPageState extends State<RoomInfoPage> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
- final images = widget.booking.rooms
- .map((room) => room.imageAssets)
- .toList(); // Use booking image
- final room =
- widget.booking.rooms.isNotEmpty ? widget.booking.rooms.first : null;
- final facilities = room?.roomFacilities.toList() ?? <FacilityType>[];
+ final images = [widget.booking.image];
+ final room = Room.empty();
+ final facilities = <FacilityType>[];
final hasFacilities = facilities.isNotEmpty;
- final description = room?.description ?? '';
+ const description = '';
final height = MediaQuery.of(context).size.height;
return BlocBuilder<ChooseShareRoomCubit, ChooseShareRoomState>(
@@ -58,8 +55,8 @@ class _RoomInfoPageState extends State<RoomInfoPage> {
minHeight: height - kComwellAppBarHeight - 80,
),
child: Column(
- children: [
- RoomImageCarousel(images: images.expand((x) => x).toList()),
+ children: [
+ // RoomImageCarousel(images: images.expand((x) => x).toList()),
Container(
width: double.infinity,
decoration: const BoxDecoration(
@@ -79,12 +76,12 @@ class _RoomInfoPageState extends State<RoomInfoPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- room?.name ?? '',
+ room.name,
style: theme.textTheme.headlineLarge,
),
- if (room?.tags.isNotEmpty ?? false)
+ if (room.tags.isNotEmpty)
TagWidget(
- text: '${room?.tags.first} M2',
+ text: '${room.tags.first} M2',
textColor: sandColor),
],
),
@@ -120,7 +117,7 @@ class _RoomInfoPageState extends State<RoomInfoPage> {
runSpacing: 8,
children: [
...facilities.map((f) => FacilityIconText(
- facility: f as FacilityType, showDivider: true)),
+ facility: f, showDivider: true)),
GestureDetector(
onTap: () =>{},
// _showFacilitiesSheet(context, facilities),
diff --git a/comwell_key_app/lib/choose_share_room/pages/share_room_base_page_template.dart b/comwell_key_app/lib/choose_share_room/pages/share_room_base_page_template.dart
new file mode 100644
index 00000000..f20cd453
--- /dev/null
+++ b/comwell_key_app/lib/choose_share_room/pages/share_room_base_page_template.dart
@@ -0,0 +1,171 @@
+
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:go_router/go_router.dart';
+
+class ShareRoomBasePageTemplate extends StatelessWidget {
+ final Booking booking;
+ final bool isShared;
+ final void Function() onClicked;
+
+ const ShareRoomBasePageTemplate({super.key, required this.booking, required this.onClicked, required this.isShared});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Scaffold(
+ backgroundColor: sandColor,
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ const SizedBox(width: 52),
+ Text(isShared ? 'received_shared_room_page_title'.tr() : 'share_room_page_title'.tr(),
+ style: theme.textTheme.headlineMedium
+ ?.copyWith(color: Colors.white)),
+ !isShared ?
+ Padding(
+ padding: const EdgeInsets.only(right: 16),
+ child: ElevatedButton(
+ onPressed: () {
+ context.pop();
+ },
+ style: ElevatedButton.styleFrom(
+ minimumSize: const Size(36, 36),
+ padding: EdgeInsets.zero,
+ backgroundColor: Colors.white,
+ shape: const CircleBorder(),
+ elevation: 0,
+ ),
+ child: const Icon(
+ Icons.close,
+ color: Colors.black,
+ size: 24,
+ ),
+ ),
+ ) : const SizedBox(width: 52)
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32),
+ child: Card(
+ color: Colors.white,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20),
+ ),
+ elevation: 2,
+ margin: const EdgeInsets.symmetric(horizontal: 24),
+ clipBehavior: Clip.antiAlias,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Stack(
+ children: [
+ ClipRRect(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(20),
+ topRight: Radius.circular(20),
+ ),
+ child: Image.asset(
+ 'assets/images/borupgaard.png',
+ height: 200,
+ width: double.infinity,
+ fit: BoxFit.cover,
+ ),
+ ),
+ Positioned(
+ top: 16,
+ left: 16,
+ child: Text(
+ booking.hotelName,
+ style: theme.textTheme.headlineLarge
+ ?.copyWith(color: Colors.white),
+ ),
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(booking.roomType,
+ style: theme.textTheme.headlineMedium),
+ const SizedBox(height: 4),
+ Text(
+ booking.roomNumber,
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: colorHeadlineText,
+ ),
+ ),
+ const SizedBox(height: 16),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SvgPicture.asset('assets/icons/user-circle.svg',
+ width: 24, height: 24),
+ const SizedBox(width: 4),
+ Text(
+ '${booking.adults} ${booking.adults > 1 ? 'adults'.tr() : 'adult'.tr()}${booking.children > 0 ? ' | ${booking.children} ${booking.children > 1 ? 'children'.tr() : 'child'.tr()}' : ''}',
+ style: theme.textTheme.bodySmall,
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ if (booking.addOnItems != null && booking.addOnItems!.isNotEmpty)
+ Text(
+ 'addon'.tr(),
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: colorHeadlineText,
+ ),
+ ),
+ const SizedBox(height: 24),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 40),
+ child: Text(
+ isShared ? 'received_shared_room_page_subtitle'.tr(args: ["${booking.firstName} ${booking.lastName}"]) : 'share_room_page_subtitle'.tr(),
+ style: Theme.of(context).textTheme.bodySmall?.copyWith(
+ color: Colors.white.withValues(alpha: 0.65),
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: onClicked,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Colors.white,
+ minimumSize: const Size.fromHeight(52),
+ elevation: 0,
+ ),
+ child: Text(isShared ? 'generic_ok'.tr() : 'share_room_page_button'.tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Colors.black,
+ )),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/choose_share_room/pages/share_room_page.dart b/comwell_key_app/lib/choose_share_room/pages/share_room_page.dart
index 3f7978c8..d4d5ae68 100644
--- a/comwell_key_app/lib/choose_share_room/pages/share_room_page.dart
+++ b/comwell_key_app/lib/choose_share_room/pages/share_room_page.dart
@@ -1,12 +1,9 @@
import 'package:comwell_key_app/choose_share_room/cubit/choose_share_room_cubit.dart';
import 'package:comwell_key_app/choose_share_room/cubit/choose_share_room_state.dart';
+import 'package:comwell_key_app/choose_share_room/pages/share_room_base_page_template.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_svg/svg.dart';
-import 'package:go_router/go_router.dart';
class ShareRoomPage extends StatelessWidget {
final Booking booking;
@@ -14,162 +11,12 @@ class ShareRoomPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final theme = Theme.of(context);
-
return BlocBuilder<ChooseShareRoomCubit, ChooseShareRoomState>(
builder: (context, state) {
final cubit = context.read<ChooseShareRoomCubit>();
- return Scaffold(
- backgroundColor: sandColor,
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- const SizedBox(width: 52),
- Text('share_room_page_title'.tr(),
- style: theme.textTheme.headlineMedium
- ?.copyWith(color: colorBackground)),
- Padding(
- padding: const EdgeInsets.only(right: 16),
- child: ElevatedButton(
- onPressed: () {
- context.pop();
- },
- style: ElevatedButton.styleFrom(
- minimumSize: const Size(36, 36),
- padding: EdgeInsets.zero,
- backgroundColor: colorBackground,
- shape: const CircleBorder(),
- elevation: 0,
- ),
- child: const Icon(
- Icons.close,
- color: colorTertiary,
- size: 24,
- ),
- ),
- )
- ],
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 32),
- child: Card(
- color: colorBackground,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(20),
- ),
- elevation: 2,
- margin: const EdgeInsets.symmetric(horizontal: 24),
- clipBehavior: Clip.antiAlias,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Stack(
- children: [
- ClipRRect(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(20),
- topRight: Radius.circular(20),
- ),
- child: Image.asset(
- 'assets/images/borupgaard.png',
- height: 200,
- width: double.infinity,
- fit: BoxFit.cover,
- ),
- ),
- Positioned(
- top: 16,
- left: 16,
- child: Text(
- booking.hotelName,
- style: theme.textTheme.headlineLarge
- ?.copyWith(color: colorBackground),
- ),
- ),
- ],
- ),
- Padding(
- padding: const EdgeInsets.all(20.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(booking.rooms.first.name,
- style: theme.textTheme.headlineMedium),
- const SizedBox(height: 4),
- Text(
- booking.rooms.first.description,
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText,
- ),
- ),
- const SizedBox(height: 16),
- Row(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- SvgPicture.asset('assets/icons/user-circle.svg',
- width: 24, height: 24),
- const SizedBox(width: 4),
- Text(
- '${booking.adults} ${booking.adults > 1 ? 'adults'.tr() : 'adult'.tr()}${booking.children > 0 ? ' | ${booking.children} ${booking.children > 1 ? 'children'.tr() : 'child'.tr()}' : ''}',
- style: theme.textTheme.bodySmall,
- ),
- ],
- ),
- const SizedBox(height: 16),
- Text(
- 'addon'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText,
- ),
- ),
-
- const SizedBox(height: 24),
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 40),
- child: Text(
- 'share_room_page_subtitle'.tr(),
- style: Theme.of(context).textTheme.bodySmall?.copyWith(
- color: colorBackground.withValues(alpha: 0.65),
- ),
- textAlign: TextAlign.center,
- ),
- ),
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: SizedBox(
- width: double.infinity,
- child: ElevatedButton(
- onPressed: () {
- cubit.shareRoom(booking);
- },
- style: ElevatedButton.styleFrom(
- backgroundColor: colorBackground,
- minimumSize: const Size.fromHeight(52),
- elevation: 0,
- ),
- child: Text('share_room_page_button'.tr(),
- style: theme.textTheme.bodyMedium?.copyWith(
- color: colorTertiary,
- )),
- ),
- ),
- ),
- ],
- ),
- ),
- );
+ return ShareRoomBasePageTemplate(booking: booking, onClicked: () {
+ cubit.shareRoom(booking);
+ }, isShared: false);
}
);
}
diff --git a/comwell_key_app/lib/common/components/comwell_error_widget.dart b/comwell_key_app/lib/common/components/comwell_error_widget.dart
index 12c6f0b0..93645b56 100644
--- a/comwell_key_app/lib/common/components/comwell_error_widget.dart
+++ b/comwell_key_app/lib/common/components/comwell_error_widget.dart
@@ -6,11 +6,13 @@ class ComwellErrorWidget extends StatelessWidget {
final String title;
final String subtitle;
final bool border;
+ final bool small;
const ComwellErrorWidget(
{super.key,
required this.title,
required this.subtitle,
- this.border = false});
+ this.border = false,
+ this.small = false});
@override
Widget build(BuildContext context) {
@@ -27,7 +29,15 @@ class ComwellErrorWidget extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
- child: Column(
+ child: small ? Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset('assets/icons/ic_error.svg',
+ width: 24, height: 24),
+ const SizedBox(width: 12),
+ Text(subtitle, style: theme.textTheme.headlineSmall, textAlign: TextAlign.center),
+ ],
+ ) : Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
diff --git a/comwell_key_app/lib/common/components/comwell_text_field.dart b/comwell_key_app/lib/common/components/comwell_text_field.dart
index 69230466..23290718 100644
--- a/comwell_key_app/lib/common/components/comwell_text_field.dart
+++ b/comwell_key_app/lib/common/components/comwell_text_field.dart
@@ -1,5 +1,6 @@
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
class ComwellTextField extends StatefulWidget {
final String fieldName;
@@ -9,6 +10,7 @@ class ComwellTextField extends StatefulWidget {
final TextEditingController controller;
final String? errorMessage;
final void Function(String)? onChanged;
+ final List<TextInputFormatter>? inputFormatters;
const ComwellTextField({
super.key,
@@ -19,6 +21,7 @@ class ComwellTextField extends StatefulWidget {
this.errorMessage,
this.textInputType,
this.onChanged,
+ this.inputFormatters,
});
@override
@@ -66,6 +69,7 @@ class ComwellTextFieldState extends State<ComwellTextField> {
focusNode: _focusNode,
keyboardType: widget.textInputType,
onChanged: widget.onChanged,
+ inputFormatters: widget.inputFormatters,
decoration: InputDecoration(
errorText: errorMessage,
errorStyle: Theme.of(context)
diff --git a/comwell_key_app/lib/common/components/shimmer_loader/share_room_shimmer_loader.dart b/comwell_key_app/lib/common/components/shimmer_loader/share_room_shimmer_loader.dart
new file mode 100644
index 00000000..e1d76765
--- /dev/null
+++ b/comwell_key_app/lib/common/components/shimmer_loader/share_room_shimmer_loader.dart
@@ -0,0 +1,177 @@
+import 'package:flutter/material.dart';
+import 'package:shimmer/shimmer.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+
+class ShareRoomShimmerLoader extends StatelessWidget {
+ const ShareRoomShimmerLoader({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: sandColor[10],
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ // Loading title
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ const SizedBox(width: 52),
+ Shimmer.fromColors(
+ baseColor: sandColor[40]!,
+ highlightColor: sandColor[10]!,
+ enabled: true,
+ child: Container(
+ width: 200,
+ height: 24,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ),
+ ),
+ const SizedBox(width: 52),
+ ],
+ ),
+
+ // Loading card
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32),
+ child: Card(
+ color: Colors.white,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20),
+ ),
+ elevation: 2,
+ margin: const EdgeInsets.symmetric(horizontal: 24),
+ clipBehavior: Clip.antiAlias,
+ child: Shimmer.fromColors(
+ baseColor: sandColor[40]!,
+ highlightColor: sandColor[10]!,
+ enabled: true,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Loading image placeholder
+ Container(
+ height: 200,
+ width: double.infinity,
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(20),
+ topRight: Radius.circular(20),
+ ),
+ ),
+ ),
+
+ // Loading content
+ Padding(
+ padding: const EdgeInsets.all(20.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Room name placeholder
+ Container(
+ width: 150,
+ height: 20,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ),
+ const SizedBox(height: 4),
+ // Room description placeholder
+ Container(
+ width: 200,
+ height: 14,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ),
+ const SizedBox(height: 16),
+ // Guest info placeholder
+ Row(
+ children: [
+ Container(
+ width: 24,
+ height: 24,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ const SizedBox(width: 4),
+ Container(
+ width: 120,
+ height: 14,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ // Addon placeholder
+ Container(
+ width: 80,
+ height: 14,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ),
+ const SizedBox(height: 24),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+
+ // Loading subtitle
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 40),
+ child: Shimmer.fromColors(
+ baseColor: sandColor[40]!,
+ highlightColor: sandColor[10]!,
+ enabled: true,
+ child: Container(
+ width: 250,
+ height: 16,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ),
+ ),
+ ),
+
+ // Loading button
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Shimmer.fromColors(
+ baseColor: sandColor[40]!,
+ highlightColor: sandColor[10]!,
+ enabled: true,
+ child: Container(
+ width: double.infinity,
+ height: 52,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/common/template_pages/payment_page_template.dart b/comwell_key_app/lib/common/template_pages/payment_page_template.dart
index 275b17f7..ae0812bf 100644
--- a/comwell_key_app/lib/common/template_pages/payment_page_template.dart
+++ b/comwell_key_app/lib/common/template_pages/payment_page_template.dart
@@ -1,6 +1,7 @@
import 'package:comwell_key_app/check_out/components/accept_terms_toggle.dart';
import 'package:comwell_key_app/check_out/components/apply_club_points.dart';
import 'package:comwell_key_app/check_out/components/checkout_itemized_bill.dart';
+import 'package:comwell_key_app/check_out/components/send_receipt.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:flutter/material.dart';
@@ -14,10 +15,12 @@ class PaymentPageTemplate extends StatelessWidget {
final int trimmedBalance;
final int clubPoints;
final bool isTermsAccepted;
+ final String userEmail;
final void Function(bool) onAcceptTermsChanged;
final bool showError;
final void Function() onShowTermsAndConditions;
final void Function(bool) onApplyClubPointsChanged;
+
const PaymentPageTemplate(
{super.key,
required this.addOnItems,
@@ -27,6 +30,7 @@ class PaymentPageTemplate extends StatelessWidget {
required this.trimmedBalance,
required this.clubPoints,
required this.isTermsAccepted,
+ required this.userEmail,
required this.onAcceptTermsChanged,
required this.showError,
required this.onShowTermsAndConditions,
@@ -65,9 +69,16 @@ class PaymentPageTemplate extends StatelessWidget {
clubPoints: clubPoints,
onApplyClubPointsChanged: onApplyClubPointsChanged,
),
- const SizedBox(height: 20),
const Divider(color: colorDivider),
const SizedBox(height: 20),
+ SendReceipt(
+ sendReceipt: true,
+ userEmail: userEmail,
+ onSendReceiptChanged: (value) {
+ // TODO: Implement send receipt
+ },
+ ),
+ const SizedBox(height: 10),
AcceptTermsToggle(
isTermsAccepted: isTermsAccepted,
onAcceptTermsChanged: onAcceptTermsChanged,
diff --git a/comwell_key_app/lib/database/comwell_db.dart b/comwell_key_app/lib/database/comwell_db.dart
index db596fe5..af68b22e 100644
--- a/comwell_key_app/lib/database/comwell_db.dart
+++ b/comwell_key_app/lib/database/comwell_db.dart
@@ -11,9 +11,12 @@ import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
+// ignore: unused_import
+// This import is required for sqlcipher_flutter_libs to override sqlite3's native library loading
+import 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart';
import 'package:sqlite3/sqlite3.dart';
import 'package:uuid/uuid.dart' as uuid;
-
+import 'package:sqlite3/open.dart';
import 'daos/bookings_dao.dart';
import 'daos/hotel_information_dao.dart';
import 'daos/user_dao.dart';
@@ -27,12 +30,14 @@ final secureStorage = SecureStorage();
BookingEntity,
UserEntity,
NotificationPermissionEntity,
- UpsaleEntity
+ UpsaleEntity,
+ HotelInformationEntity
], daos: [
BookingsDao,
UserDAO,
NotificationPermissionDAO,
- UpsalesDAO
+ UpsalesDAO,
+ HotelInformationDAO
])
class ComwellDatabase extends _$ComwellDatabase {
static const String _cipherKey = "sql_cipher";
@@ -47,8 +52,26 @@ class ComwellDatabase extends _$ComwellDatabase {
MigrationStrategy get migration => destructiveFallback;
Future<void> deleteDatabase() async {
- for (final table in allTables) {
- await table.delete().go();
+ try {
+ // Try to delete all tables first (while cipher key is still available)
+ for (final table in allTables) {
+ try {
+ await table.delete().go();
+ } catch (e) {
+ // Ignore errors for individual tables, continue with others
+ }
+ }
+ } catch (e) {
+ // If table deletion fails, try to delete the database file directly
+ try {
+ final path = await getApplicationSupportDirectory();
+ final file = File(p.join(path.path, _dbFileName));
+ if (await file.exists()) {
+ await file.delete();
+ }
+ } catch (fileError) {
+ // If file deletion also fails, ignore - database may already be deleted or inaccessible
+ }
}
}
@@ -60,28 +83,38 @@ class ComwellDatabase extends _$ComwellDatabase {
} else {
return cipher;
}
+
return getCipher();
}
static QueryExecutor _openDatabase() {
return LazyDatabase(() async {
+ // sqlcipher_flutter_libs automatically overrides sqlite3 when imported above
+ await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions();
+ open.overrideFor(OperatingSystem.android, openCipherOnAndroid);
final path = await getApplicationSupportDirectory();
final file = File(p.join(path.path, _dbFileName));
final cipher = await getCipher();
void setup(Database db) {
- final result = db.select("pragma cipher_version");
- if (result.isEmpty) {
- throw UnsupportedError(
- "This database needs to run with SQLCipher, but that library is unavailable");
+ try {
+ final result = db.select("pragma cipher_version");
+ if (result.isEmpty) {
+ throw UnsupportedError(
+ "This database needs to run with SQLCipher, but that library is unavailable");
+ }
+ db.execute("pragma key = \"$cipher\"");
+ db.execute("select count(*) from sqlite_master");
+ // Add pragmas for better stability
+ db.execute("pragma journal_mode = WAL");
+ db.execute("pragma synchronous = NORMAL");
+ db.execute("pragma cache_size = 1000");
+ db.execute("pragma temp_store = MEMORY");
+ } catch (e) {
+ // If setup fails, the database might be corrupted
+ // The error will be caught by the calling code
+ rethrow;
}
- db.execute("pragma key = \"$cipher\"");
- db.execute("select count(*) from sqlite_master");
- // Add pragmas for better stability
- db.execute("pragma journal_mode = WAL");
- db.execute("pragma synchronous = NORMAL");
- db.execute("pragma cache_size = 1000");
- db.execute("pragma temp_store = MEMORY");
}
// Use synchronous database creation instead of createInBackground
@@ -89,4 +122,17 @@ class ComwellDatabase extends _$ComwellDatabase {
return NativeDatabase(file, setup: setup);
});
}
+
+ /// Recreates the database by deleting the corrupted file
+ Future<void> recreateDatabase() async {
+ try {
+ final path = await getApplicationSupportDirectory();
+ final file = File(p.join(path.path, _dbFileName));
+ if (await file.exists()) {
+ await file.delete();
+ }
+ } catch (e) {
+ // Ignore errors during deletion
+ }
+ }
}
diff --git a/comwell_key_app/lib/database/daos/bookings_dao.dart b/comwell_key_app/lib/database/daos/bookings_dao.dart
index f4164eb0..c69041c4 100644
--- a/comwell_key_app/lib/database/daos/bookings_dao.dart
+++ b/comwell_key_app/lib/database/daos/bookings_dao.dart
@@ -1,6 +1,5 @@
import 'dart:convert';
import 'package:comwell_key_app/database/comwell_db.dart';
-import 'package:comwell_key_app/overview/models/room.dart';
import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
import 'package:comwell_key_app/services/models/bookings_dto.dart';
@@ -22,21 +21,19 @@ class BookingsDao extends DatabaseAccessor<ComwellDatabase>
}
Future<Booking> getBookingDetails(
- String bookingId, int userId, List<Room> rooms) async {
+ String bookingId) async {
final booking = await (select(bookingEntity)
..where((entity) => entity.id.equals(bookingId)))
.getSingle();
final json = jsonDecode(booking.json) as Json;
- return BookingDTO.fromJson(json).toBooking(userId, rooms);
+ return BookingDTO.fromJson(json).toBooking();
}
- Future<void> insert(
- Iterable<BookingDTO> bookings) async {
+ Future<void> insert(Iterable<BookingDTO> bookings) async {
final entities = bookings.map((booking) {
final json = jsonEncode(booking.toJson());
return BookingEntityCompanion.insert(
- id: booking.confirmationNumber,
- json: json);
+ id: booking.confirmationNumber, json: json);
});
await batch((batch) => batch.insertAll(bookingEntity, entities,
mode: InsertMode.insertOrReplace));
@@ -46,10 +43,10 @@ class BookingsDao extends DatabaseAccessor<ComwellDatabase>
await insert(bookings.current);
}
-
Stream<BookingsDTO> watchBookings() {
return (select(bookingEntity)).watch().map((entities) {
- return BookingsDTO(current: _entityToBooking(entities), past: [], cancelled: []);
+ return BookingsDTO(
+ current: _entityToBooking(entities), past: [], cancelled: []);
});
}
diff --git a/comwell_key_app/lib/database/daos/notifications_dao.dart b/comwell_key_app/lib/database/daos/notifications_dao.dart
index 51f26bcc..be55d7e0 100644
--- a/comwell_key_app/lib/database/daos/notifications_dao.dart
+++ b/comwell_key_app/lib/database/daos/notifications_dao.dart
@@ -23,7 +23,7 @@ class NotificationPermissionDAO extends DatabaseAccessor<ComwellDatabase>
final entities = permissions.map((permission) {
final json = jsonEncode(permission.toJson());
return NotificationPermissionEntityCompanion.insert(
- id: permission.id, json: json);
+ code: permission.code, json: json);
});
await batch((batch) => batch.insertAll(
notificationPermissionEntity, entities,
diff --git a/comwell_key_app/lib/database/daos/upsales_dao.dart b/comwell_key_app/lib/database/daos/upsales_dao.dart
index c1da9389..e08cede7 100644
--- a/comwell_key_app/lib/database/daos/upsales_dao.dart
+++ b/comwell_key_app/lib/database/daos/upsales_dao.dart
@@ -20,10 +20,7 @@ class UpsalesDAO extends DatabaseAccessor<ComwellDatabase>
String confirmationNumber) async {
final query = select(upsaleEntity)
..where((tbl) => tbl.id.equals(confirmationNumber));
- final result = await query.getSingleOrNull();
- if (result == null) {
- throw Exception("Up sales not found in database");
- }
+ final result = await query.getSingle();
final json = jsonDecode(result.json) as Json;
return UpSalesDTO.fromJson(json).toUpSales();
}
diff --git a/comwell_key_app/lib/database/tables/notification_table.dart b/comwell_key_app/lib/database/tables/notification_table.dart
index 2aa988d2..1025bc30 100644
--- a/comwell_key_app/lib/database/tables/notification_table.dart
+++ b/comwell_key_app/lib/database/tables/notification_table.dart
@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
@DataClassName('NotificationPermissionDb')
class NotificationPermissionEntity extends Table {
- IntColumn get id => integer().unique()();
+ TextColumn get code => text().unique()();
TextColumn get json => text()();
}
diff --git a/comwell_key_app/lib/find_booking/confirmation_id_formatter.dart b/comwell_key_app/lib/find_booking/confirmation_id_formatter.dart
new file mode 100644
index 00000000..c4127dab
--- /dev/null
+++ b/comwell_key_app/lib/find_booking/confirmation_id_formatter.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/services.dart';
+
+class ConfirmationIdFormatter extends TextInputFormatter {
+ @override
+ TextEditingValue formatEditUpdate(
+ TextEditingValue oldValue,
+ TextEditingValue newValue,
+ ) {
+ // Extract only digits from the input
+ String digitsOnly = newValue.text.replaceAll(RegExp(r'[^\d]'), '');
+
+ // Limit to 10 digits maximum
+ if (digitsOnly.length > 9) {
+ digitsOnly = digitsOnly.substring(0, 9);
+ }
+
+ // Build the formatted string: add dash after 9 digits
+ String formatted = '';
+ if (digitsOnly.length > 8) {
+ formatted = '${digitsOnly.substring(0, 8)}-${digitsOnly.substring(8)}';
+ } else if (digitsOnly.length == 8) {
+ formatted = '$digitsOnly-';
+ } else {
+ formatted = digitsOnly;
+ }
+
+ // Calculate cursor position
+ int cursorPosition = formatted.length;
+ if (newValue.text.length < oldValue.text.length) {
+ // When deleting, maintain cursor position relative to digits
+ cursorPosition = formatted.length;
+ } else {
+ // When adding, place cursor after the last digit
+ if (digitsOnly.length == 8) {
+ cursorPosition = formatted.length; // After the dash
+ } else if (digitsOnly.length > 8) {
+ cursorPosition = formatted.length; // After the last digit
+ } else {
+ cursorPosition = digitsOnly.length;
+ }
+ }
+
+ return TextEditingValue(
+ text: formatted,
+ selection: TextSelection.collapsed(offset: cursorPosition),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/find_booking/cubit/find_booking_cubit.dart b/comwell_key_app/lib/find_booking/cubit/find_booking_cubit.dart
new file mode 100644
index 00000000..aafe967f
--- /dev/null
+++ b/comwell_key_app/lib/find_booking/cubit/find_booking_cubit.dart
@@ -0,0 +1,49 @@
+import 'package:comwell_key_app/find_booking/cubit/find_booking_state.dart';
+import 'package:comwell_key_app/find_booking/find_booking_repository.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/utils/locator.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class FindBookingCubit extends Cubit<FindBookingState> {
+ final FindBookingRepository findBookingRepository =
+ locator<FindBookingRepository>();
+ final TextEditingController confirmationIdController =
+ TextEditingController();
+ final TextEditingController lastNameController = TextEditingController();
+
+ FindBookingCubit() : super(FindBookingState()) {
+ confirmationIdController.addListener(() {
+ emit(state.copyWith(isButtonEnabled: canContinue));
+ });
+
+ lastNameController.addListener(() {
+ emit(state.copyWith(isButtonEnabled: canContinue));
+ });
+ }
+
+ Future<Booking?> findBooking(String confirmationId, String lastName) async {
+ try {
+ emit(FindBookingState(isLoading: true));
+ final booking = await findBookingRepository.findBookingByConfirmationId(
+ confirmationId, lastName);
+ return booking;
+ } catch (e) {
+ emit(FindBookingState(error: e.toString()));
+ return null;
+ }
+ }
+
+ bool get isConfirmationIdValid {
+ // Format: 9 digits + dash + 1 digit = 11 characters total
+ final text = confirmationIdController.text;
+ if (text.length != 10) return false;
+ // Check format: 9 digits, dash, 1 digit
+ final regex = RegExp(r'^\d{8}-\d$');
+ return regex.hasMatch(text);
+ }
+
+ bool get canContinue {
+ return isConfirmationIdValid && lastNameController.text.isNotEmpty;
+ }
+}
diff --git a/comwell_key_app/lib/find_booking/cubit/find_booking_state.dart b/comwell_key_app/lib/find_booking/cubit/find_booking_state.dart
new file mode 100644
index 00000000..f1bbcbeb
--- /dev/null
+++ b/comwell_key_app/lib/find_booking/cubit/find_booking_state.dart
@@ -0,0 +1,18 @@
+class FindBookingState {
+ final bool isLoading;
+ final bool isButtonEnabled;
+ final String? error;
+
+ FindBookingState({this.isLoading = false, this.isButtonEnabled = false, this.error});
+
+ FindBookingState copyWith({bool? isLoading, bool? isButtonEnabled, String? error}) {
+ return FindBookingState(
+ isLoading: isLoading ?? this.isLoading,
+ isButtonEnabled: isButtonEnabled ?? this.isButtonEnabled,
+ error: error ?? this.error,
+ );
+ }
+
+ @override
+ List<Object?> get props => [isLoading, isButtonEnabled, error];
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/find_booking/find_booking_page.dart b/comwell_key_app/lib/find_booking/find_booking_page.dart
index c01ff800..36a2796d 100644
--- a/comwell_key_app/lib/find_booking/find_booking_page.dart
+++ b/comwell_key_app/lib/find_booking/find_booking_page.dart
@@ -1,6 +1,7 @@
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
import 'package:comwell_key_app/common/components/comwell_text_field.dart';
-import 'package:comwell_key_app/overview/cubit/overview_cubit.dart';
+import 'package:comwell_key_app/find_booking/cubit/find_booking_cubit.dart';
+import 'package:comwell_key_app/find_booking/cubit/find_booking_state.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -18,216 +19,97 @@ class FindBookingPage extends StatefulWidget {
class FindBookingPageState extends State<FindBookingPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
- final TextEditingController _bookingReferenceController =
- TextEditingController();
- final TextEditingController _lastNameController = TextEditingController();
-
- bool get _isButtonEnabled {
- return _bookingReferenceController.text.isNotEmpty &&
- _lastNameController.text.isNotEmpty;
- }
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
- _bookingReferenceController.addListener(_updateButtonState);
- _lastNameController.addListener(_updateButtonState);
}
@override
void dispose() {
- _bookingReferenceController.dispose();
- _lastNameController.dispose();
_tabController.dispose();
super.dispose();
}
- void _updateButtonState() {
- setState(() {});
- }
-
- Future<void> _findBooking(BuildContext context) async {
- final OverviewCubit overviewCubit = BlocProvider.of<OverviewCubit>(context);
- await overviewCubit.findBooking(
- _bookingReferenceController.text, _lastNameController.text);
- // Navigate to the loading page
- if (context.mounted) {
- context.pushNamed(AppRoutes.loadingPage.name);
- }
-
- await Future<void>.delayed(const Duration(seconds: 3));
-
- if (context.mounted) {
- context.pop();
- }
- }
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
- return Scaffold(
- appBar: const ComwellAppBar(),
- backgroundColor: Theme.of(context).colorScheme.surface,
- body: Padding(
- padding: const EdgeInsets.all(16.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ return BlocBuilder<FindBookingCubit, FindBookingState>(
+ builder: (context, state) {
+ final cubit = context.read<FindBookingCubit>();
+ return Scaffold(
+ appBar: const ComwellAppBar(),
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ body: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ 'Find Booking',
+ style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600),
+ ),
+ const SizedBox(height: 20),
+ const SizedBox(height: 20),
+ ComwellTextField(
+ fieldName: 'booking_reference'.tr(),
+ initialValue: '',
+ readOnly: false,
+ controller: cubit.confirmationIdController,
+ textInputType: const TextInputType.numberWithOptions(signed: true),
+ ),
+ const SizedBox(height: 20),
+ ComwellTextField(
+ fieldName: 'your_last_name'.tr(),
+ initialValue: '',
+ readOnly: false,
+ controller: cubit.lastNameController,
+ ),
+ const Spacer(),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ),
+ bottomNavigationBar: Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- const Text(
- 'Find Booking',
- style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600),
- ),
- const SizedBox(height: 20),
- /* GestureDetector(
- onTap: () {
- showCupertinoModalBottomSheet(
- backgroundColor: Colors.white,
- barrierColor: colorTertiary.withOpacity(0.5),
- topRadius: const Radius.circular(30),
- context: context,
- builder: (context) => Scaffold(
- backgroundColor: Colors.transparent,
- body: Column(
- children: [
- Padding(
- padding: const EdgeInsets.only(top: 40, left: 16),
- child: Container(
- height: 47,
- color: Colors.white,
- child: Stack(
- children: [
- Align(
- alignment: Alignment.topRight,
- child: RoundIconButton(
- icon: 'assets/icons/close-icon.svg',
- color: sandColor[20]!,
- onPressed: () {
- context.pop();
- }),
- ),
- Align(
- alignment: Alignment.bottomLeft,
- child: Text('hotels'.tr(),
- style: theme.textTheme.titleLarge),
- )
- ],
- ),
- ),
- ),
- const SizedBox(height: 20),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TabBar(
- controller: _tabController,
- indicatorPadding:
- const EdgeInsets.symmetric(vertical: 8),
- indicatorSize: TabBarIndicatorSize.tab,
- indicator: BoxDecoration(
- borderRadius: BorderRadius.circular(25),
- color: sandColor,
- ),
- labelColor: Colors.white,
- labelStyle: Theme.of(context).textTheme.labelLarge,
- unselectedLabelColor: colorTertiary,
- dividerColor: Colors.transparent,
- tabAlignment: TabAlignment.start,
- isScrollable: true,
- tabs: [
- Tab(
- text: 'all'.tr(),
- ),
- Tab(text: 'zealand'.tr()),
- Tab(text: 'funen'.tr()),
- Tab(
- text: "jutland".tr(),
- )
- ],
- ),
- ),
- const Divider(color: colorDivider),
- Expanded(child: HotelTabView(hotels: hotels)),
- const Divider(color: colorDivider),
- const SizedBox(height: 0),
- Center(
- child: ElevatedButton(
- onPressed: () {
- // Handle find booking action
- },
- child: const Text('Find Booking'),
- ),
- ),
- ],
- ),
+ const Divider(color: colorDivider),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 16, right: 16, top: 16, bottom: 32),
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ minimumSize: const Size(double.infinity, 50),
+ backgroundColor: sandColor,
+ disabledBackgroundColor: disabledButtonColor,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(25),
),
- );
- },
- child: Container(
- padding:
- const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
- decoration: BoxDecoration(
- border: Border.all(color: colorDivider),
- borderRadius: BorderRadius.circular(5),
),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('choose_hotel'.tr()),
- const Icon(Icons.arrow_drop_down),
- ],
+ onPressed: cubit.canContinue
+ ? () async {
+ await cubit.findBooking(
+ cubit.confirmationIdController.text,
+ cubit.lastNameController.text);
+ if (context.mounted) {
+ context.pushNamed(AppRoutes.loadingPage.name);
+ }
+ }
+ : null,
+ child: Text(
+ 'Find Booking',
+ style: theme.textTheme.headlineSmall?.copyWith(
+ color: Colors.white,
+ ),
),
),
- ), */
- const SizedBox(height: 20),
- ComwellTextField(
- fieldName: 'booking_reference'.tr(),
- initialValue: '',
- readOnly: false,
- controller: _bookingReferenceController,
),
- const SizedBox(height: 20),
- ComwellTextField(
- fieldName: 'your_last_name'.tr(),
- initialValue: '',
- readOnly: false,
- controller: _lastNameController,
- ),
- const Spacer(),
- const SizedBox(height: 20),
],
),
- ),
- bottomNavigationBar: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Divider(color: colorDivider),
- Padding(
- padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 32),
- child: ElevatedButton(
- style: ElevatedButton.styleFrom(
- minimumSize: const Size(double.infinity, 50),
- backgroundColor: sandColor,
- disabledBackgroundColor: disabledButtonColor,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(25),
- ),
- ),
- onPressed: _isButtonEnabled
- ? () async {
- await _findBooking(context);
- }
- : null,
- child: Text(
- 'Find Booking',
- style: theme.textTheme.headlineSmall?.copyWith(
- color: Colors.white,
- ),
- ),
- ),
- ),
- ],
- ),
- );
+ );
+ });
}
}
diff --git a/comwell_key_app/lib/find_booking/find_booking_repository.dart b/comwell_key_app/lib/find_booking/find_booking_repository.dart
new file mode 100644
index 00000000..6e6be575
--- /dev/null
+++ b/comwell_key_app/lib/find_booking/find_booking_repository.dart
@@ -0,0 +1,12 @@
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/services/api.dart';
+import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
+
+class FindBookingRepository {
+ final Api api = Api();
+
+ Future<Booking> findBookingByConfirmationId(String confirmationId, String lastName) async {
+ final bookingDTO = await api.findBookingByConfirmationId(confirmationId, lastName);
+ return bookingDTO.toBooking();
+ }
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/find_booking/loading_page.dart b/comwell_key_app/lib/find_booking/loading_page.dart
index a00bd252..f1f7f3b7 100644
--- a/comwell_key_app/lib/find_booking/loading_page.dart
+++ b/comwell_key_app/lib/find_booking/loading_page.dart
@@ -1,5 +1,9 @@
+import 'package:comwell_key_app/find_booking/cubit/find_booking_cubit.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:lottie/lottie.dart';
@@ -18,13 +22,15 @@ class LoadingPageState extends State<LoadingPage> {
}
Future<void> _startLoading() async {
- await Future<void>.delayed(const Duration(seconds: 3)); // Minimum 3 seconds delay
- if (mounted) {
- context.pop(); // Pop the loading page
- // Pop the previous page
- // Show result or error popup here
- // For example:
- // showDialog(context: context, builder: (context) => _buildResultDialog());
+ await Future<void>.delayed(
+ const Duration(seconds: 3)); // Minimum 3 seconds delay
+ if (!mounted) return;
+ final cubit = context.read<FindBookingCubit>();
+ final response = await cubit.findBooking(
+ cubit.confirmationIdController.text, cubit.lastNameController.text);
+
+ if (mounted && response is Booking) {
+ context.pushNamed(AppRoutes.overview.name);
}
}
@@ -34,7 +40,7 @@ class LoadingPageState extends State<LoadingPage> {
backgroundColor: sandColor,
body: Center(
child: Lottie.asset(
- 'assets/animations/load_animation.json', // Replace with your Lottie animation file
+ 'assets/animations/load_animation.json',
width: 64,
height: 64,
fit: BoxFit.cover,
@@ -43,4 +49,4 @@ class LoadingPageState extends State<LoadingPage> {
),
);
}
-}
\ No newline at end of file
+}
diff --git a/comwell_key_app/lib/key/key_page.dart b/comwell_key_app/lib/key/key_page.dart
index b5a97504..ce7c30b6 100644
--- a/comwell_key_app/lib/key/key_page.dart
+++ b/comwell_key_app/lib/key/key_page.dart
@@ -10,7 +10,9 @@ import 'package:go_router/go_router.dart';
import 'package:lottie/lottie.dart';
class KeyPage extends StatelessWidget {
- const KeyPage({super.key});
+ final String roomNumber;
+
+ const KeyPage({super.key, required this.roomNumber});
@override
Widget build(BuildContext context) {
@@ -52,9 +54,29 @@ class KeyPage extends StatelessWidget {
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
- child: Image.asset(
- 'assets/images/room_key_with_number.png',
- fit: BoxFit.cover,
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ Image.asset(
+ 'assets/images/room_key.png',
+ fit: BoxFit.cover,
+ ),
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
+ decoration: BoxDecoration(
+ border: Border.all(color: Colors.white, width: 2),
+ borderRadius: BorderRadius.circular(96),
+ ),
+ child: Text(
+ "key_page_room_prefix".tr(args: [roomNumber]),
+ style: Theme.of(context).textTheme.displayLarge?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ fontSize: 36,
+ ),
+ ),
+ ),
+ ],
),
),
const SizedBox(height: 20),
diff --git a/comwell_key_app/lib/main.dart b/comwell_key_app/lib/main.dart
index 19a1a843..e4a93574 100644
--- a/comwell_key_app/lib/main.dart
+++ b/comwell_key_app/lib/main.dart
@@ -1,3 +1,4 @@
+import 'dart:io';
import 'package:comwell_key_app/authentication/authentication_repository.dart';
import 'package:comwell_key_app/utils/firebase.dart';
import 'package:comwell_key_app/utils/locator.dart';
@@ -7,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
+import 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart';
import 'comwell_app.dart';
import 'firebase_options_dev.dart' as fb_dev;
import 'firebase_options_stage.dart' as fb_stage;
@@ -27,10 +29,14 @@ void main() async {
void runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
WidgetsFlutterBinding.ensureInitialized();
+
+
+
await EasyLocalization.ensureInitialized();
debugPrint("Current flavor: $appFlavor");
debugPrint("Loading environment file: $envFile");
+
try {
await dotenv.load(fileName: envFile);
debugPrint("Successfully loaded environment file");
diff --git a/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart b/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
index d75177a8..adf890dd 100644
--- a/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
+++ b/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
@@ -83,7 +83,7 @@ class MyBookingCubit extends Cubit<MyBookingState> {
_tracking.trackBeginCheckout(analyticsEventItem);
try {
await paymentServicesCubit.createSession(booking.balance?.toInt() ?? 0,
- booking.confirmationId, state.applyClubPoints);
+ booking.confirmationNumber, state.applyClubPoints, booking.hotelCode);
await Future<void>.delayed(const Duration(milliseconds: 4000));
} catch (e) {
emit(state.setError());
diff --git a/comwell_key_app/lib/my_booking/my_booking_page.dart b/comwell_key_app/lib/my_booking/my_booking_page.dart
index d24a2d10..e723c5e3 100644
--- a/comwell_key_app/lib/my_booking/my_booking_page.dart
+++ b/comwell_key_app/lib/my_booking/my_booking_page.dart
@@ -3,7 +3,6 @@ import 'package:comwell_key_app/booking_details/booking_details_repository.dart'
import 'package:comwell_key_app/booking_details/components/share_button.dart';
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
import 'package:comwell_key_app/housekeeping/housekeeping_repository.dart';
-import 'package:comwell_key_app/my_booking/components/balance_bottom_sheet.dart';
import 'package:comwell_key_app/my_booking/cubit/my_booking_cubit.dart';
import 'package:comwell_key_app/my_booking/cubit/my_booking_state.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
@@ -11,12 +10,14 @@ import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
import 'package:comwell_key_app/payment/cubit/payment_processing_state.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
+import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:comwell_key_app/up_sales/up_sales_repository.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_svg/flutter_svg.dart';
class MyBookingPage extends StatelessWidget {
final Booking booking;
@@ -28,7 +29,7 @@ class MyBookingPage extends StatelessWidget {
return BlocBuilder<MyBookingCubit, MyBookingState>(
builder: (context, state) {
- final cubit = context.read<MyBookingCubit>();
+
return BlocListener<PaymentCubit, PaymentProcessingState>(
listener: (context, state) async {
@@ -68,7 +69,7 @@ class MyBookingPage extends StatelessWidget {
),
),
Text(
- booking.id,
+ booking.confirmationNumber,
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 16),
@@ -86,27 +87,6 @@ class MyBookingPage extends StatelessWidget {
),
),
),
- Positioned(
- bottom: 0,
- left: 0,
- right: 0,
- child: GestureDetector(
- onTap: () {
- showModalBottomSheet<void>(
- isScrollControlled: true,
- context: context,
- builder: (context) => BalanceBottomSheet(
- balance: cubit.totalAddonBalance,
- userName: booking.booker.name,
- booking: booking,
- addOnItems: booking.addOnItems ?? [],
- cubit: cubit,
- ),
- );
- },
- child: _buildBalanceBottomSheet(theme, cubit),
- ),
- ),
],
),
);
@@ -168,7 +148,7 @@ class MyBookingPage extends StatelessWidget {
style: theme.textTheme.bodySmall?.copyWith(
color: colorHeadlineText,
)),
- Text(booking.roomType, style: theme.textTheme.bodyMedium),
+ Text(booking.toRoomType(), style: theme.textTheme.bodyMedium),
const SizedBox(height: 16),
const Divider(color: colorDivider),
Text(
@@ -188,7 +168,8 @@ class MyBookingPage extends StatelessWidget {
color: colorHeadlineText,
),
),
- Text(booking.booker.name, style: theme.textTheme.bodyMedium),
+ Text("${booking.firstName} ${booking.lastName}",
+ style: theme.textTheme.bodyMedium),
const SizedBox(height: 8),
const Divider(color: colorDivider),
Row(
@@ -219,10 +200,11 @@ class MyBookingPage extends StatelessWidget {
houseKeepingRepository: locator<HouseKeepingRepository>(),
),
child: ShareButton(
+ isMyBooking: true,
guests: booking.guests,
buttonColor: sandColor[40],
- userButtonSize: 40,
- userButtonOverlap: 10,
+ userButtonSize: 30,
+ userButtonOverlap: 5,
),
),
),
@@ -259,7 +241,7 @@ class MyBookingPage extends StatelessWidget {
const SizedBox(width: 8),
],
),
- Image.asset('assets/images/master.png', width: 42, height: 42),
+ SvgPicture.asset('assets/images/master.svg', width: 30, height: 30),
],
),
const SizedBox(height: 16),
@@ -295,47 +277,4 @@ class MyBookingPage extends StatelessWidget {
],
);
}
-
- Widget _buildBalanceBottomSheet(ThemeData theme, MyBookingCubit cubit) {
- return Container(
- height: 100,
- decoration: const BoxDecoration(
- color: sandColor,
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16),
- ),
- ),
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- 'my_balance'.tr(),
- style: theme.textTheme.bodyLarge?.copyWith(
- color: Colors.white,
- fontWeight: FontWeight.w600,
- ),
- ),
- Row(
- children: [
- Text(
- '${cubit.totalAddonBalance} kr',
- style: theme.textTheme.bodyLarge?.copyWith(
- color: Colors.white,
- fontWeight: FontWeight.w600,
- ),
- ),
- const SizedBox(width: 8),
- Icon(
- Icons.keyboard_arrow_up,
- color: Colors.white.withValues(alpha: 0.8),
- ),
- ],
- ),
- ],
- ),
- );
- }
}
diff --git a/comwell_key_app/lib/my_booking/pages/my_booking_payment_page.dart b/comwell_key_app/lib/my_booking/pages/my_booking_payment_page.dart
index 51a271db..7aad0e25 100644
--- a/comwell_key_app/lib/my_booking/pages/my_booking_payment_page.dart
+++ b/comwell_key_app/lib/my_booking/pages/my_booking_payment_page.dart
@@ -40,6 +40,7 @@ class MyBookingPaymentPage extends StatelessWidget {
trimmedBalance: trimmedBalance,
clubPoints: clubPoints,
isTermsAccepted: isTermsAccepted,
+ userEmail: cubit.booking.bookerLastName,
onAcceptTermsChanged: onAcceptTermsChanged,
showError: showError,
onShowTermsAndConditions: onShowTermsAndConditions,
diff --git a/comwell_key_app/lib/notifications/components/communications_list.dart b/comwell_key_app/lib/notifications/components/communications_list.dart
index 07526e67..4939dd21 100644
--- a/comwell_key_app/lib/notifications/components/communications_list.dart
+++ b/comwell_key_app/lib/notifications/components/communications_list.dart
@@ -23,12 +23,21 @@ class CommunicationsList extends StatelessWidget {
itemCount: notificationPermissions.length,
itemBuilder: (context, index) {
return SwitchListTile(
- activeThumbColor: sandColor,
- inactiveTrackColor: Colors.grey[200],
- inactiveThumbColor: Colors.white,
+ thumbColor: WidgetStateProperty.resolveWith((states) {
+ if (states.contains(WidgetState.selected)) {
+ return sandColor;
+ }
+ return Colors.white;
+ }),
+ trackColor: WidgetStateProperty.resolveWith((states) {
+ if (!states.contains(WidgetState.selected)) {
+ return Colors.grey[200];
+ }
+ return null;
+ }),
trackOutlineColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.surface),
title: Text(
- notificationPermissions.elementAt(index).name,
+ notificationPermissions.elementAt(index).displayName,
style: theme.textTheme.titleMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w600,
@@ -45,9 +54,9 @@ class CommunicationsList extends StatelessWidget {
fontWeight: FontWeight.w400,
),
),
- value: notificationPermissions.elementAt(index).allowed,
+ value: notificationPermissions.elementAt(index).given,
onChanged: (bool value) {
- valueChanged(notificationPermissions.elementAt(index).name);
+ valueChanged(notificationPermissions.elementAt(index).displayName);
},
contentPadding: EdgeInsets.zero,
);
diff --git a/comwell_key_app/lib/notifications/cubit/notifications_cubit.dart b/comwell_key_app/lib/notifications/cubit/notifications_cubit.dart
index 8fcbf5d5..2a732a8f 100644
--- a/comwell_key_app/lib/notifications/cubit/notifications_cubit.dart
+++ b/comwell_key_app/lib/notifications/cubit/notifications_cubit.dart
@@ -18,16 +18,16 @@ class NotificationsCubit extends Cubit<NotificationsState> {
// List of permission types that should be shown in the UI
final List<NotificationPermissionType> _visiblePermissionTypes = [
- NotificationPermissionType.b2bNewsletter,
+ NotificationPermissionType.appNotifications,
NotificationPermissionType.ccSms,
NotificationPermissionType.ccDigital,
- NotificationPermissionType.companyNotifications,
+ NotificationPermissionType.ccEmail,
];
Iterable<NotificationPermission> _filterVisiblePermissions(
Iterable<NotificationPermission> permissions) {
return permissions.where((permission) => _visiblePermissionTypes
- .any((type) => type.notificationPermissionId == permission.id));
+ .any((type) => type.notificationPermissionId == permission.code));
}
void init() async {
@@ -37,9 +37,8 @@ class NotificationsCubit extends Cubit<NotificationsState> {
error: null,
));
try {
- user = await notificationsRepository.fetchUser();
notificationPermissions =
- await notificationsRepository.fetchNotificationPermissions(user.id);
+ await notificationsRepository.fetchNotificationPermissions();
notificationPermissions =
_filterVisiblePermissions(notificationPermissions);
@@ -59,9 +58,9 @@ class NotificationsCubit extends Cubit<NotificationsState> {
void onNotificationPermissionClicked(String name) {
notificationPermissions = notificationPermissions
- .map((permission) => permission.name == name
+ .map((permission) => permission.displayName == name
? permission.copyWith(
- allowed: !permission.allowed,
+ given: !permission.given,
notificationPermissionDescription:
permission.notificationPermissionDescription)
: permission)
@@ -77,7 +76,7 @@ class NotificationsCubit extends Cubit<NotificationsState> {
void updateAllPermissionsUI(bool value) {
notificationPermissions = notificationPermissions
.map((permission) => permission.copyWith(
- allowed: value,
+ given: value,
notificationPermissionDescription:
permission.notificationPermissionDescription,
))
@@ -91,10 +90,9 @@ class NotificationsCubit extends Cubit<NotificationsState> {
}
void updatePreferences(
- Iterable<NotificationPermission> notificationPermissions,
- int guestId) async {
+ Iterable<NotificationPermission> notificationPermissions) async {
await notificationsRepository.updateNotificationPreferences(
- guestId, notificationPermissions);
+ notificationPermissions);
emit(NotificationsState(
allNotifications: notificationPermissions,
@@ -103,9 +101,9 @@ class NotificationsCubit extends Cubit<NotificationsState> {
));
}
- void fetchNotificationPermissions(int guestId) async {
+ void fetchNotificationPermissions() async {
notificationPermissions =
- await notificationsRepository.fetchNotificationPermissions(guestId);
+ await notificationsRepository.fetchNotificationPermissions();
emit(NotificationsState(
allNotifications: notificationPermissions,
diff --git a/comwell_key_app/lib/notifications/models/notification_permission.dart b/comwell_key_app/lib/notifications/models/notification_permission.dart
index a545a21f..c03d3ea3 100644
--- a/comwell_key_app/lib/notifications/models/notification_permission.dart
+++ b/comwell_key_app/lib/notifications/models/notification_permission.dart
@@ -16,28 +16,29 @@ enum NotificationPermissionType {
appNotifications,
similarProducts;
- int get notificationPermissionId {
+ String get notificationPermissionId {
switch (this) {
- case NotificationPermissionType.b2bNewsletter:
- return 1; //1400
+ case NotificationPermissionType.appNotifications:
+ return "ccmarketingapp";
case NotificationPermissionType.ccEmail:
- return 1405; //1405
+ return "ccmarketingemail"; //1405
case NotificationPermissionType.ccDigital:
- return 1406; //1406
+ return "ccmarketingdigi"; //1406
case NotificationPermissionType.ccSms:
- return 2;
+ return "ccmarketingsms";
+
+ case NotificationPermissionType.b2bNewsletter:
+ return "b2bmarketingnewsletter"; //1400
case NotificationPermissionType.b2bDigital:
- return 1408;
+ return "b2bmarketingdigi";
case NotificationPermissionType.companyEmail:
- return 1409;
+ return "companymarketingemail";
case NotificationPermissionType.companyNotifications:
- return 1410;
+ return "companymarketingnewsletter";
case NotificationPermissionType.stayEmails:
- return 1411;
- case NotificationPermissionType.appNotifications:
- return 1412;
- case NotificationPermissionType.similarProducts:
- return 1413;
+ return "staymarketingemail";
+ case NotificationPermissionType.similarProducts:
+ return "similarproducts";
}
}
@@ -73,7 +74,7 @@ enum NotificationPermissionType {
case NotificationPermissionType.b2bNewsletter:
return 'company_deal_subtitle'.tr();
case NotificationPermissionType.ccEmail:
- return '';
+ return 'email_subtitle'.tr();
case NotificationPermissionType.ccDigital:
return 'digital_media_subtitle'.tr();
case NotificationPermissionType.ccSms:
@@ -87,7 +88,7 @@ enum NotificationPermissionType {
case NotificationPermissionType.stayEmails:
return '';
case NotificationPermissionType.appNotifications:
- return '';
+ return 'push_subtitle'.tr();
case NotificationPermissionType.similarProducts:
return '';
}
@@ -96,16 +97,16 @@ enum NotificationPermissionType {
@JsonSerializable()
class NotificationPermission {
- final int id;
- final String name;
+ final String code;
+ final String displayName;
final String? notificationPermissionDescription;
- final bool allowed;
+ final bool given;
NotificationPermission({
- required this.id,
- required this.name,
+ required this.code,
+ required this.displayName,
this.notificationPermissionDescription,
- required this.allowed,
+ required this.given,
});
factory NotificationPermission.fromJson(Json json) =>
@@ -114,16 +115,16 @@ class NotificationPermission {
Json toJson() => _$NotificationPermissionToJson(this);
NotificationPermission copyWith(
- {bool? allowed, String? notificationPermissionDescription}) =>
+ {bool? given, String? notificationPermissionDescription}) =>
NotificationPermission(
- id: id,
- name: name,
+ code: code,
+ displayName: displayName,
notificationPermissionDescription: notificationPermissionDescription,
- allowed: allowed ?? this.allowed,
+ given: given ?? this.given,
);
@override
String toString() {
- return 'NotificationPermission{id: $id, name: $name, allowed: $allowed, notificationPermissionDescription: $notificationPermissionDescription}';
+ return 'NotificationPermission{code: $code, displayName: $displayName, given: $given}';
}
}
diff --git a/comwell_key_app/lib/notifications/notifications_page.dart b/comwell_key_app/lib/notifications/notifications_page.dart
index 26ab6164..4cec8158 100644
--- a/comwell_key_app/lib/notifications/notifications_page.dart
+++ b/comwell_key_app/lib/notifications/notifications_page.dart
@@ -69,13 +69,22 @@ class NotificationsPage extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Switch(
- activeThumbColor: sandColor,
- inactiveTrackColor: Colors.grey[200],
- inactiveThumbColor: Colors.white,
+ thumbColor: WidgetStateProperty.resolveWith((states) {
+ if (states.contains(WidgetState.selected)) {
+ return sandColor;
+ }
+ return Colors.white;
+ }),
+ trackColor: WidgetStateProperty.resolveWith((states) {
+ if (!states.contains(WidgetState.selected)) {
+ return Colors.grey[200];
+ }
+ return null;
+ }),
trackOutlineColor: WidgetStatePropertyAll(
Theme.of(context).colorScheme.surface),
value: cubit.state.allNotifications
- .every((e) => e.allowed),
+ .every((e) => e.given),
onChanged: (bool value) {
cubit.updateAllPermissionsUI(value);
},
@@ -119,7 +128,7 @@ class NotificationsPage extends StatelessWidget {
child: ElevatedButton(
onPressed: () {
cubit.updatePreferences(
- cubit.state.allNotifications, cubit.user.id);
+ cubit.state.allNotifications);
},
style: theme.elevatedButtonTheme.style,
child: Text(
diff --git a/comwell_key_app/lib/notifications/notifications_repository.dart b/comwell_key_app/lib/notifications/notifications_repository.dart
index caff4c8d..974bd6c2 100644
--- a/comwell_key_app/lib/notifications/notifications_repository.dart
+++ b/comwell_key_app/lib/notifications/notifications_repository.dart
@@ -17,22 +17,21 @@ class NotificationsRepository {
return user;
}
- Future<void> updatePreferences(int guestId,
+ Future<void> updatePreferences(
Iterable<NotificationPermission> notificationPermissions) async {
- return api.updateNotificationPreferences(guestId, notificationPermissions);
+ return api.updateNotificationPreferences(notificationPermissions);
}
- Future<Iterable<NotificationPermission>> fetchNotificationPermissions(
- int guestId) async {
+ Future<Iterable<NotificationPermission>> fetchNotificationPermissions() async {
notificationPermissions =
await locator<ComwellDatabase>().notificationPermissionDAO.getNotificationPermissions();
if (notificationPermissions.isEmpty) {
- final response = await api.getNotificationPermissions(guestId);
+ final response = await api.getNotificationPermissions();
final data = response.data as Map<String, dynamic>;
- notificationPermissions = (data['permissions'] as List<dynamic>)
+ notificationPermissions = (data['codes'] as List<dynamic>)
.map((json) => NotificationPermission.fromJson(json as Json))
.toList();
@@ -54,7 +53,7 @@ class NotificationsRepository {
Iterable<NotificationPermission> permissions) {
return permissions.map((permission) {
final enumValue = NotificationPermissionType.values.firstWhere(
- (type) => type.notificationPermissionId == permission.id,
+ (type) => type.notificationPermissionId == permission.code,
);
return permission.copyWith(
notificationPermissionDescription:
@@ -63,11 +62,11 @@ class NotificationsRepository {
}).toList();
}
- Future<dynamic> updateNotificationPreferences(int guestId,
+ Future<dynamic> updateNotificationPreferences(
Iterable<NotificationPermission> notificationPermissions) async {
await locator<ComwellDatabase>().notificationPermissionDAO
.saveNotificationPermission(notificationPermissions);
- return api.updateNotificationPreferences(guestId, notificationPermissions);
+ return api.updateNotificationPreferences(notificationPermissions);
}
}
diff --git a/comwell_key_app/lib/overview/components/bill_download_modal.dart b/comwell_key_app/lib/overview/components/bill_download_modal.dart
new file mode 100644
index 00000000..ff779e22
--- /dev/null
+++ b/comwell_key_app/lib/overview/components/bill_download_modal.dart
@@ -0,0 +1,72 @@
+import 'package:comwell_key_app/common/components/comwell_text_field.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class BillDownloadModal extends StatelessWidget {
+ final TextEditingController emailController = TextEditingController();
+ final String initialEmail;
+ final VoidCallback onDownload;
+
+ BillDownloadModal(
+ {super.key, required this.onDownload, required this.initialEmail});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return ClipRRect(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16),
+ ),
+ child: Container(
+ color: Colors.white,
+ child: SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text('download_bill'.tr(), style: theme.textTheme.titleLarge),
+ IconButton(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ icon: Icon(Icons.close, color: colorHeadlineText, size: 24),
+ style: IconButton.styleFrom(
+ backgroundColor: sandColor[40],
+ shape: const CircleBorder(),
+ elevation: 0,
+ minimumSize: const Size(40, 40),
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ ComwellTextField(
+ fieldName: 'bill_email_placeholder'.tr(),
+ initialValue: initialEmail,
+ readOnly: false,
+ controller: emailController),
+ const SizedBox(height: 24),
+ const Divider(color: colorDivider),
+ const SizedBox(height: 16),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: onDownload,
+ child: Text('send_to_email'.tr(),
+ style: theme.textTheme.headlineSmall
+ ?.copyWith(color: Colors.white))),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/overview/components/booking_list_item_view.dart b/comwell_key_app/lib/overview/components/booking_list_item_view.dart
index 90ee0160..e4ee11c8 100644
--- a/comwell_key_app/lib/overview/components/booking_list_item_view.dart
+++ b/comwell_key_app/lib/overview/components/booking_list_item_view.dart
@@ -29,7 +29,7 @@ class BookingListItemView extends StatelessWidget {
final theme = Theme.of(context);
return InkWell(
onTap: () => context.pushNamed(AppRoutes.pastCancelledBookings.name,
- extra: booking),
+ extra: [booking, isCancelled]),
child: Container(
height: 66,
margin: const EdgeInsets.only(bottom: 8, left: 8, right: 8),
@@ -94,8 +94,7 @@ class BookingListItemView extends StatelessWidget {
if (isCancelled)
Text(
"booking_annulled".tr(),
- style:
- theme.textTheme.bodyMedium?.copyWith(color: Colors.red),
+ style: theme.textTheme.bodyMedium?.copyWith(color: Colors.red),
),
const SizedBox(width: 16),
],
diff --git a/comwell_key_app/lib/overview/components/current_booking_list_item_view.dart b/comwell_key_app/lib/overview/components/current_booking_list_item_view.dart
index 9ff66971..51ee8d6b 100644
--- a/comwell_key_app/lib/overview/components/current_booking_list_item_view.dart
+++ b/comwell_key_app/lib/overview/components/current_booking_list_item_view.dart
@@ -1,9 +1,15 @@
+
+import 'package:comwell_key_app/overview/components/guest_list_circles.dart';
import 'package:comwell_key_app/overview/components/prepare_room_widget.dart';
+import 'package:comwell_key_app/overview/cubit/overview_cubit.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/overview/models/guest.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/booking_details/components/guest_list.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
@@ -15,6 +21,10 @@ class CurrentBookingListItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
+ const double userButtonSize = 45;
+ const double userButtonOverlap = 10;
+ final overviewCubit = context.read<OverviewCubit>();
+
return Container(
height: booking.reservationStatus == ReservationStatus.newreservation
? 327
@@ -36,8 +46,31 @@ class CurrentBookingListItem extends StatelessWidget {
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
- child: Image.asset(booking.image,
- height: 180, width: double.infinity, fit: BoxFit.fill)),
+ child: Stack(
+ children: [
+ Image.asset(booking.image,
+ height: 180, width: double.infinity, fit: BoxFit.fill),
+ // Guest circles in the top-left corner
+ if (booking.guests.length > 1)
+ Positioned(
+ top: 16,
+ left: 16,
+ child: GuestListCirclesWidget(
+ guests: booking.guests,
+ userButtonSize: userButtonSize,
+ userButtonOverlap: userButtonOverlap,
+ onTap: () {
+ _showGuestList(
+ overviewCubit,
+ context,
+ booking,
+ booking.guests,
+ "${booking.firstName} ${booking.lastName}");
+ },
+ ),
+ ),
+ ],
+ )),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
@@ -99,3 +132,83 @@ class CurrentBookingListItem extends StatelessWidget {
);
}
}
+
+Future<dynamic> _showGuestList(
+ OverviewCubit overviewCubit,
+ BuildContext context,
+ Booking booking,
+ Iterable<Guest> guests,
+ String bookerName) async {
+ final theme = Theme.of(context);
+ final guestListWithoutBooker = guests
+ .where((guest) => "${guest.firstName} ${guest.lastName}" != bookerName)
+ .toList();
+
+ return showModalBottomSheet<dynamic>(
+ context: context,
+ backgroundColor: colorBackground,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16),
+ ),
+ ),
+ isScrollControlled: false,
+ builder: (BuildContext bottomSheetContext) {
+ return ClipRRect(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16),
+ ),
+ child: Scaffold(
+ body: SizedBox(
+ width: double.infinity,
+ height: 450,
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16.0, vertical: 8.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text('handle_guests_title'.tr(),
+ style: theme.textTheme.titleLarge?.copyWith(
+ color: colorTertiary,
+ fontWeight: FontWeight.w600,
+ )),
+ ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: sandColor[40],
+ shape: const CircleBorder(),
+ elevation: 0,
+ minimumSize: const Size(40, 40)),
+ child: const Icon(Icons.close, color: colorTertiary),
+ onPressed: () {
+ Navigator.pop(bottomSheetContext);
+ },
+ ),
+ ],
+ ),
+ ),
+ Expanded(
+ child: GuestList(
+ isOverview: true,
+ guests: guestListWithoutBooker,
+ selectedGuests: const [],
+ onGuestSelected: (_) {},
+ onGuestRemoved: (int guestId) {
+ overviewCubit.removeGuestsFromBooking(
+ booking.confirmationNumber,
+ booking.hotelCode,
+ guestId);
+ },
+ )),
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ );
+}
diff --git a/comwell_key_app/lib/overview/components/guest_list_circles.dart b/comwell_key_app/lib/overview/components/guest_list_circles.dart
new file mode 100644
index 00000000..43642527
--- /dev/null
+++ b/comwell_key_app/lib/overview/components/guest_list_circles.dart
@@ -0,0 +1,60 @@
+import 'package:comwell_key_app/overview/models/guest.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/utils/share_button_utils.dart';
+import 'package:flutter/material.dart';
+
+class GuestListCirclesWidget extends StatelessWidget {
+ final Iterable<Guest> guests;
+ final double userButtonSize;
+ final double userButtonOverlap;
+ final VoidCallback onTap;
+
+ const GuestListCirclesWidget({
+ super.key,
+ required this.guests,
+ required this.userButtonSize,
+ required this.userButtonOverlap,
+ required this.onTap,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final numberOfUsers = guests.length;
+ final allInitials = generateInitials(guests).toList();
+
+ return GestureDetector(
+ behavior: HitTestBehavior.opaque,
+ onTap: onTap,
+ child: SizedBox(
+ width: numberOfUsers * (userButtonSize - userButtonOverlap) + userButtonOverlap,
+ height: userButtonSize,
+ child: Stack(
+ children: List.generate(
+ numberOfUsers,
+ (index) => Positioned(
+ left: (numberOfUsers - 1 - index) * (userButtonSize - userButtonOverlap),
+ child: Container(
+ width: userButtonSize,
+ height: userButtonSize,
+ decoration: BoxDecoration(
+ color: index % 2 == 0 ? sandColor : colorTertiary,
+ shape: BoxShape.circle,
+ ),
+ child: Center(
+ child: Text(
+ allInitials.elementAt(index),
+ style: const TextStyle(
+ color: colorBackground,
+ fontWeight: FontWeight.w400,
+ fontSize: 18,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/overview/cubit/overview_cubit.dart b/comwell_key_app/lib/overview/cubit/overview_cubit.dart
index 9bb6d0c4..52017b25 100644
--- a/comwell_key_app/lib/overview/cubit/overview_cubit.dart
+++ b/comwell_key_app/lib/overview/cubit/overview_cubit.dart
@@ -2,6 +2,9 @@ import 'package:bloc/bloc.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/overview/models/bookings.dart';
import 'package:comwell_key_app/overview/repository/overview_repository.dart';
+import 'package:collection/collection.dart';
+import 'package:comwell_key_app/share/share_booking_repository.dart';
+import 'package:comwell_key_app/utils/locator.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
@@ -9,14 +12,16 @@ part 'overview_state.dart';
class OverviewCubit extends Cubit<OverviewState> {
final OverviewRepository overviewRepository;
- OverviewCubit(this.overviewRepository)
- : super(OverviewInitial());
+ final ShareBookingRepository shareBookingRepository =
+ locator<ShareBookingRepository>();
+ OverviewCubit(this.overviewRepository) : super(OverviewInitial());
Future<void> fetchBookings() async {
emit(OverviewLoading());
+
try {
final bookings = await overviewRepository.fetchAllBookingsForUser();
-
+
emit(OverviewLoaded(bookings: bookings));
} catch (e, st) {
if (kDebugMode) print("err=$e, $st");
@@ -30,16 +35,26 @@ class OverviewCubit extends Cubit<OverviewState> {
final updatedBookings = List<Booking>.from(currentState.bookings.current);
updatedBookings.add(newBooking);
-
- final updatedBookingsObject = Bookings(current: updatedBookings, past: const [], cancelled: const []);
+ final updatedBookingsObject = Bookings(
+ current: updatedBookings, past: const [], cancelled: const []);
emit(OverviewLoaded(bookings: updatedBookingsObject));
} else {
- final bookings = Bookings(current: [newBooking], past: const [], cancelled: const []);
+ final bookings =
+ Bookings(current: [newBooking], past: const [], cancelled: const []);
emit(OverviewLoaded(bookings: bookings));
}
}
+ Booking? getCheckedInBooking() {
+ if (state is OverviewLoaded) {
+ final currentState = state as OverviewLoaded;
+ return currentState.bookings.current.firstWhereOrNull((booking) =>
+ booking.reservationStatus == ReservationStatus.checkedin);
+ }
+ return null;
+ }
+
Future<Booking?> findBooking(String bookingReference, String lastName) async {
try {
final booking =
@@ -51,7 +66,8 @@ class OverviewCubit extends Cubit<OverviewState> {
final currentState = state as OverviewLoaded;
emit(OverviewNoBookingFound(bookings: currentState.bookings));
} else {
- emit(const OverviewNoBookingFound(bookings: Bookings(current: [], past: [], cancelled: [])));
+ emit(const OverviewNoBookingFound(
+ bookings: Bookings(current: [], past: [], cancelled: [])));
}
} catch (e) {
emit(OverviewError(error: e.toString()));
@@ -69,4 +85,18 @@ class OverviewCubit extends Cubit<OverviewState> {
emit(OverviewError(error: e.toString()));
}
}
+
+ Future<void> removeGuestsFromBooking(
+ String hmsConfirmationNumber, String hotelCode, int guestId) async {
+ emit(OverviewLoading());
+ try {
+ await shareBookingRepository.removeGuestsFromBooking(
+ hmsConfirmationNumber, hotelCode, guestId);
+ final bookings = await overviewRepository.fetchAllBookingsForUser();
+ emit(OverviewLoaded(bookings: bookings));
+ } catch (e) {
+ if (kDebugMode) print("err=$e");
+ emit(OverviewError(error: e.toString()));
+ }
+ }
}
diff --git a/comwell_key_app/lib/overview/models/booking.dart b/comwell_key_app/lib/overview/models/booking.dart
index 30ca6c08..84f8e3b1 100644
--- a/comwell_key_app/lib/overview/models/booking.dart
+++ b/comwell_key_app/lib/overview/models/booking.dart
@@ -1,13 +1,11 @@
-
import 'package:comwell_key_app/overview/models/guest.dart';
import 'package:comwell_key_app/overview/models/payment_details.dart';
-import 'package:comwell_key_app/overview/models/room.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
import 'package:equatable/equatable.dart';
class Booking extends Equatable {
final String id;
- final String confirmationId;
+ final String confirmationNumber;
final String roomNumber;
final DateTime startDate;
final DateTime endDate;
@@ -15,60 +13,50 @@ class Booking extends Equatable {
final String image;
final String hotelName;
final String hotelCode;
- final String hmsConfirmationNumber;
+ final String firstName;
+ final String lastName;
+ final String bookerFirstName;
+ final String bookerLastName;
final String roomType;
final int adults;
final int children;
- final Guest booker;
final DateTime bookingDate;
final bool digitalCard;
final Iterable<Guest> guests;
- final num? totalCharge;
final num? balance;
+ final bool isPrimaryGuest;
final String? maskedCardNumber;
- final List<Room> rooms;
final List<BookingAddonItem>? addOnItems;
Booking({
required this.id,
- required this.confirmationId,
+ required this.confirmationNumber,
required this.roomNumber,
required this.startDate,
required this.endDate,
required this.reservationStatus,
required this.image,
required this.hotelName,
+ required this.firstName,
+ required this.lastName,
+ required this.bookerFirstName,
+ required this.bookerLastName,
required this.roomType,
required this.children,
required this.adults,
required this.hotelCode,
- required this.booker,
required this.bookingDate,
required this.digitalCard,
- required this.totalCharge,
required this.balance,
+ required this.isPrimaryGuest,
required this.maskedCardNumber,
- required this.rooms,
- required this.hmsConfirmationNumber,
Iterable<Guest>? guests,
required this.addOnItems,
- }) : guests = _ensureBookerInGuestList(booker, guests ?? []);
-
- static Iterable<Guest> _ensureBookerInGuestList(
- Guest booker, Iterable<Guest> guests) {
- final bookerExists = guests.any((guest) => guest.id == booker.id);
- if (!bookerExists) {
- return [
- booker,
- ...guests,
- ];
- }
- return guests;
- }
+ }) : guests = guests ?? [];
@override
String toString() {
- return "Booking(id: $id, confirmationId: $confirmationId, roomNumber: $roomNumber, startDate: $startDate, endDate: $endDate, reservationStatus: $reservationStatus, image: $image, hotelName: $hotelName, hotelCode: $hotelCode, roomType: $roomType, adults: $adults, children: $children, booker: $booker, bookingDate: $bookingDate, digitalCard: $digitalCard, guests: $guests, addOnItems: $addOnItems, balance: $balance, totalCharge: $totalCharge, maskedCardNumber: $maskedCardNumber, hmsConfirmationNumber: $hmsConfirmationNumber)";
+ return "Booking(id: $id, ConfirmationNumber: $confirmationNumber, roomNumber: $roomNumber, startDate: $startDate, endDate: $endDate, reservationStatus: $reservationStatus, image: $image, hotelName: $hotelName, hotelCode: $hotelCode, roomType: $roomType, adults: $adults, children: $children, bookingDate: $bookingDate, digitalCard: $digitalCard, guests: $guests, addOnItems: $addOnItems, isPrimaryGuest: $isPrimaryGuest, totalCharge: $balance, maskedCardNumber: $maskedCardNumber)";
}
@override
@@ -83,16 +71,13 @@ class Booking extends Equatable {
children,
adults,
hotelCode,
- booker,
bookingDate,
guests,
- totalCharge,
balance,
+ isPrimaryGuest,
maskedCardNumber,
reservationStatus,
- rooms,
addOnItems,
- hmsConfirmationNumber,
];
Booking copyWith({
@@ -105,17 +90,19 @@ class Booking extends Equatable {
String? image,
String? hotelName,
String? hotelCode,
- String? hmsConfirmationNumber,
String? roomType,
int? adults,
int? children,
- Guest? booker,
+ String? primaryGuestFirstName,
+ String? primaryGuestLastName,
+ String? bookerFirstName,
+ String? bookerLastName,
+ bool? isPrimaryGuest,
DateTime? bookingDate,
PaymentDetails? paymentDetails,
- String? confirmationId,
+ String? confirmationNumber,
Iterable<Guest>? guests,
num? totalCharge,
- List<Room>? rooms,
List<BookingAddonItem>? addOnItems,
}) {
return Booking(
@@ -127,26 +114,28 @@ class Booking extends Equatable {
image: image ?? this.image,
hotelName: hotelName ?? this.hotelName,
hotelCode: hotelCode ?? this.hotelCode,
- hmsConfirmationNumber: hmsConfirmationNumber ?? this.hmsConfirmationNumber,
+ firstName: firstName,
+ lastName: lastName,
+ bookerFirstName: bookerFirstName ?? this.bookerFirstName,
+ bookerLastName: bookerLastName ?? this.bookerLastName,
roomType: roomType ?? this.roomType,
adults: adults ?? this.adults,
children: children ?? this.children,
- booker: booker ?? this.booker,
bookingDate: bookingDate ?? this.bookingDate,
- confirmationId: confirmationId ?? this.confirmationId,
+ confirmationNumber: confirmationNumber ?? this.confirmationNumber,
digitalCard: digitalCard,
guests: guests ?? this.guests,
- totalCharge: totalCharge ?? this.totalCharge,
- balance: balance,
+ balance: totalCharge ?? balance,
+ isPrimaryGuest: isPrimaryGuest ?? this.isPrimaryGuest,
maskedCardNumber: maskedCardNumber,
- rooms: rooms ?? this.rooms,
addOnItems: addOnItems ?? this.addOnItems,
);
}
Booking updateGuests(Iterable<String> guestNames) {
- final updatedGuests = guests.where(
- (guest) => guest.id == booker.id || !guestNames.contains(guest.name));
+ final updatedGuests = guests.where((guest) =>
+ !guestNames.contains(guest.firstName) &&
+ !guestNames.contains(guest.lastName));
return copyWith(guests: updatedGuests);
}
@@ -170,4 +159,3 @@ enum ReservationStatus {
return status;
}
}
-
diff --git a/comwell_key_app/lib/overview/models/guest.dart b/comwell_key_app/lib/overview/models/guest.dart
index f12cee4c..39f14c57 100644
--- a/comwell_key_app/lib/overview/models/guest.dart
+++ b/comwell_key_app/lib/overview/models/guest.dart
@@ -1,20 +1,11 @@
-import 'package:comwell_key_app/utils/json.dart';
import 'package:equatable/equatable.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part '../../.generated/overview/models/guest.g.dart';
-
-@JsonSerializable()
class Guest extends Equatable {
- final String name;
- final String id;
-
- const Guest({required this.name, required this.id});
-
- factory Guest.fromJson(Json json) => _$GuestFromJson(json);
+ final String firstName;
+ final String lastName;
+ final int id;
- Json toJson() => _$GuestToJson(this);
+ const Guest({required this.firstName, required this.lastName, required this.id});
@override
- List<Object?> get props => [name, id];
+ List<Object?> get props => [firstName, lastName, id];
}
diff --git a/comwell_key_app/lib/overview/models/room.dart b/comwell_key_app/lib/overview/models/room.dart
index 4b324dde..3bca30d5 100644
--- a/comwell_key_app/lib/overview/models/room.dart
+++ b/comwell_key_app/lib/overview/models/room.dart
@@ -25,4 +25,17 @@ class Room {
String toString() {
return 'Room(confirmationNumber: $confirmationNumber, assignedTo: $assignedTo, name: $name, description: $description, guests: $guests, imageAssets: $imageAssets, roomFacilities: $roomFacilities, tags: $tags)';
}
+
+ static Room empty() {
+ return Room(
+ confirmationNumber: '',
+ assignedTo: '',
+ name: '',
+ description: '',
+ guests: 0,
+ imageAssets: [],
+ roomFacilities: [],
+ tags: [],
+ );
+ }
}
\ No newline at end of file
diff --git a/comwell_key_app/lib/overview/overview_page.dart b/comwell_key_app/lib/overview/overview_page.dart
index ca027789..30c79e2a 100644
--- a/comwell_key_app/lib/overview/overview_page.dart
+++ b/comwell_key_app/lib/overview/overview_page.dart
@@ -22,12 +22,12 @@ class OverviewPage extends StatefulWidget {
OverviewTabViewState createState() => OverviewTabViewState();
}
-class OverviewTabViewState extends State<OverviewPage>
- with SingleTickerProviderStateMixin {
+class OverviewTabViewState extends State<OverviewPage> with WidgetsBindingObserver, SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
+ WidgetsBinding.instance.addObserver(this);
super.initState();
_tabController = TabController(length: 3, vsync: this);
@@ -38,9 +38,47 @@ class OverviewTabViewState extends State<OverviewPage>
@override
void dispose() {
_tabController.dispose();
+ WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ if (state == AppLifecycleState.resumed && mounted) {
+ _checkAndNavigateToBookingDetails();
+ }
+ }
+
+ void _checkAndNavigateToBookingDetails() {
+ if (!mounted) return;
+
+ // Only check if the overview state is loaded
+ final overviewCubit = context.read<OverviewCubit>();
+ if (overviewCubit.state is! OverviewLoaded) {
+ return; // Don't check if bookings aren't loaded yet
+ }
+
+ // Only execute if the user is on the overview page
+ final router = GoRouter.of(context);
+ final currentLocation = router.routerDelegate.currentConfiguration.last.matchedLocation;
+
+ // Check if the current location is the overview page
+ // The matchedLocation format for overview is "overview"
+ final isOnOverviewPage = currentLocation == AppRoutes.overview.name ||
+ currentLocation == '/${AppRoutes.overview.name}';
+
+ if (!isOnOverviewPage) {
+ return; // Only execute if user is on the overview page
+ }
+
+ final checkedInBooking = overviewCubit.getCheckedInBooking();
+
+ if (checkedInBooking != null) {
+ context.pushNamed(AppRoutes.bookingDetails.name,
+ extra: checkedInBooking);
+ }
+ }
+
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@@ -53,9 +91,16 @@ class OverviewTabViewState extends State<OverviewPage>
}
});
}
+ // Check for checked-in booking when overview is loaded
+ if (state is OverviewLoaded) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ _checkAndNavigateToBookingDetails();
+ });
+ }
},
child: BlocBuilder<OverviewCubit, OverviewState>(
builder: (context, state) {
+
return Scaffold(
backgroundColor: colorBackground,
appBar: const ComwellAppBar(
diff --git a/comwell_key_app/lib/overview/past_cancelled_booking_detail_page.dart b/comwell_key_app/lib/overview/past_cancelled_booking_detail_page.dart
index f7a0b681..ae19a9ef 100644
--- a/comwell_key_app/lib/overview/past_cancelled_booking_detail_page.dart
+++ b/comwell_key_app/lib/overview/past_cancelled_booking_detail_page.dart
@@ -1,14 +1,20 @@
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
+import 'package:comwell_key_app/overview/components/bill_download_modal.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
+import 'package:comwell_key_app/themes/comwell_colors.dart' show colorError;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import '../themes/light_theme.dart';
+
class PastCancelledBookingDetailPage extends StatelessWidget {
final Booking booking;
+ final bool isCancelled;
- const PastCancelledBookingDetailPage({super.key, required this.booking});
+ const PastCancelledBookingDetailPage(
+ {super.key, required this.booking, this.isCancelled = false});
@override
Widget build(BuildContext context) {
@@ -27,15 +33,13 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
'my_booking'.tr(),
style: theme.textTheme.headlineLarge,
),
- const SizedBox(
- height: 36,
- ),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
title: Text('booking_reference'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
- subtitle: Text(booking.id, style: theme.textTheme.headlineSmall),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+ subtitle: Text(booking.confirmationNumber,
+ style: theme.textTheme.headlineSmall),
),
const Divider(color: colorDivider),
ListTile(
@@ -49,8 +53,8 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
Text(
textAlign: TextAlign.start,
'check_in'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
Text(
DateFormat('d. MMM yyyy')
.format(booking.startDate)
@@ -65,8 +69,8 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('check_out'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
Text(
DateFormat('d. MMM yyyy')
.format(booking.endDate)
@@ -80,10 +84,10 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
title: Text('booking_details'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
subtitle: Text(
- booking.roomType,
+ booking.toRoomType(),
style: theme.textTheme.headlineSmall,
),
),
@@ -91,8 +95,8 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
title: Text('number_of_guests'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
subtitle: Text(
"${booking.adults} ${booking.adults > 1 ? 'adults'.tr() : 'adult'.tr()}${booking.children >= 1 ? ', ${booking.children} ${booking.children > 1 ? 'children'.tr() : 'child'.tr()}' : ''}",
style: theme.textTheme.headlineSmall),
@@ -101,17 +105,18 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
title: Text('booker'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
- subtitle: Text(booking.booker.name,
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+ subtitle: Text(
+ "${booking.firstName} ${booking.lastName}",
style: theme.textTheme.headlineSmall),
),
const Divider(color: colorDivider),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
title: Text('booking_date'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText)),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
subtitle: Text(
DateFormat('d. MMM yyyy')
.format(booking.bookingDate)
@@ -120,22 +125,56 @@ class PastCancelledBookingDetailPage extends StatelessWidget {
const Divider(color: colorDivider),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('payment'.tr(),
+ style: theme.textTheme.headlineLarge),
+ const SizedBox(height: 16),
+ Text('payment_method'.tr(),
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: colorHeadlineText,
+ )),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text('•••• •••• •••• 1234',
+ style: theme.textTheme.bodyMedium),
+ const SizedBox(width: 8),
+ isCancelled
+ ? Text('overview_tabbar_cancelled'.tr(),
+ style: theme.textTheme.bodyMedium
+ ?.copyWith(color: colorError))
+ : SvgPicture.asset('assets/images/master.svg',
+ width: 30, height: 30),
+ ],
+ ),
+ const SizedBox(height: 8),
+ const Divider(color: colorDivider),
+ const SizedBox(height: 8),
+ Text('bill'.tr(),
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: colorHeadlineText,
+ )),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Text('bill'.tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText,
- )),
Text('get_bill'.tr(),
style: theme.textTheme.headlineSmall),
- ]),
- SvgPicture.asset('assets/icons/download_bill.svg'),
- ],
- )),
+ InkWell(
+ onTap: () {
+ showModalBottomSheet<void>(
+ context: context,
+ isScrollControlled: true,
+ builder: (context) => BillDownloadModal(
+ onDownload: () {},
+ initialEmail: 'test@test.com'));
+ },
+ child: SvgPicture.asset(
+ 'assets/icons/download_bill.svg')),
+ ],
+ ),
+ ])),
const Divider(color: colorDivider)
],
),
diff --git a/comwell_key_app/lib/overview/repository/overview_repository.dart b/comwell_key_app/lib/overview/repository/overview_repository.dart
index e6f7c509..e90f72cf 100644
--- a/comwell_key_app/lib/overview/repository/overview_repository.dart
+++ b/comwell_key_app/lib/overview/repository/overview_repository.dart
@@ -2,13 +2,11 @@ import 'package:comwell_key_app/choose_share_room/choose_share_room_repository.d
import 'package:comwell_key_app/database/comwell_db.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/overview/models/bookings.dart';
-import 'package:comwell_key_app/overview/models/guest.dart';
-import 'package:comwell_key_app/overview/models/room.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
-import 'package:comwell_key_app/utils/locator.dart';
+import 'package:comwell_key_app/utils/locator.dart' show locator, registerDatabase;
class OverviewRepository {
final api = Api();
@@ -21,23 +19,39 @@ class OverviewRepository {
try {
final currentBookings = await api.fetchCurrentBookingsForUser();
- //TODO: Implement past and cancelled bookings
+ final pastBookings = await api.fetchPastBookingsForUser();
+ final cancelledBookings = await api.fetchCancelledBookingsForUser();
- // final pastBookings = await api.fetchPastBookingsForUser();
- // final cancelledBookings = await api.fetchCancelledBookingsForUser();
- final pastBookings = <Booking>[];
- final cancelledBookings = <Booking>[];
-
- final user = await profileRepository.fetchProfileSettings();
- final rooms = await chooseShareRoomRepository.getMockRooms();
- await locator<ComwellDatabase>().bookingsDao.insert(currentBookings);
+ final database = locator<ComwellDatabase>();
+ try {
+ await database.bookingsDao.insert(currentBookings);
+ } catch (dbError) {
+ // Check if it's a database corruption error
+ if (dbError.toString().contains('file is not a database') ||
+ dbError.toString().contains('code 26') ||
+ dbError.toString().contains('SqliteException')) {
+ // Recreate the database and try again
+ await database.recreateDatabase();
+ // Unregister and re-register the database to get a fresh instance
+ if (locator.isRegistered<ComwellDatabase>()) {
+ locator.resetLazySingleton<ComwellDatabase>();
+ }
+ registerDatabase();
+ final newDatabase = locator<ComwellDatabase>();
+ await newDatabase.bookingsDao.insert(currentBookings);
+ } else {
+ rethrow;
+ }
+ }
final bookings = Bookings(
current:
- currentBookings.map((e) => e.toBooking(user.id, rooms)).toList(),
- past: pastBookings,
- cancelled: cancelledBookings);
-
+ currentBookings.map((e) => e.toBooking()).toList(),
+ past: pastBookings.map((e) => e.toBooking()).toList(),
+ cancelled: cancelledBookings
+ .map((e) => e.toBooking())
+ .toList());
+
return bookings;
} catch (e) {
throw Exception('Failed to fetch bookings $e');
@@ -46,14 +60,15 @@ class OverviewRepository {
Future<Booking?> findBooking(String bookingReference, String lastName) async {
// needs implementation
- final user = await locator<ComwellDatabase>().userDAO.getUser();
final dto = BookingDTO(
roomNumber: "1234",
hotelCode: "CBO",
firstName: "Hello",
lastName: "World",
+ bookerFirstName: "Hello",
+ bookerLastName: "World",
+ guests: [GuestDTO(id: 1, firstName: "Hello", lastName: "World")],
confirmationNumber: "12345",
- hmsConfirmationNumber: "1234",
dayIn: "31-12-2000",
dayOut: "31-12-2000",
cancelTime: "31-12-2000",
@@ -63,48 +78,39 @@ class OverviewRepository {
roomType: "??",
adults: 3,
children: 5,
- totalCharge: 12345,
- balance: 0,
+ balance: 12345,
+ isPrimaryGuest: false,
maskedCardNumber: "1234567890",
addOnItems: [
BookingAddonItem("addOnItem1", "addOnItem1", 1, 100),
BookingAddonItem("addOnItem2", "addOnItem2", 1, 200),
BookingAddonItem("addOnItem3", "addOnItem3", 1, 300)
]);
- return dto.toBooking(user.id, []);
+ return dto.toBooking();
}
final mockBookings = [1, 2, 3].map((i) => Booking(
id: "id$i",
- confirmationId: "confirmationId$i",
+ confirmationNumber: "crmConfirmationNumber$i",
roomNumber: "roomNumber$i",
startDate: DateTime.now(),
endDate: DateTime.now(),
- hmsConfirmationNumber: "hmsConfirmationNumber$i",
reservationStatus: ReservationStatus.newreservation,
image: "assets/images/no_current_bookings_background.jpeg",
hotelName: "hotelName$i",
roomType: "roomType$i",
- totalCharge: 100,
+ balance: 100,
children: 3,
adults: 3,
hotelCode: "hotelCode$i",
- booker: const Guest(id: "id", name: "name"),
+ firstName: "firstName",
+ lastName: "lastName",
+ bookerFirstName: "bookerFirstName",
+ bookerLastName: "bookerLastName",
bookingDate: DateTime.now(),
digitalCard: false,
- balance: 0,
+ isPrimaryGuest: false,
maskedCardNumber: "1234567890",
- rooms: [
- Room(
- confirmationNumber: "CONF00$i",
- assignedTo: "assignedTo$i",
- name: "roomName$i",
- description: "roomDescription$i",
- guests: 3,
- imageAssets: ["roomImageAsset$i"],
- roomFacilities: [],
- tags: ["10 M2"]),
- ],
addOnItems: [
BookingAddonItem("addOnItem$i", "addOnItem$i", 1, 100)
]));
diff --git a/comwell_key_app/lib/payment/cubit/payment_cubit.dart b/comwell_key_app/lib/payment/cubit/payment_cubit.dart
index c7125e16..cf573618 100644
--- a/comwell_key_app/lib/payment/cubit/payment_cubit.dart
+++ b/comwell_key_app/lib/payment/cubit/payment_cubit.dart
@@ -10,13 +10,13 @@ class PaymentCubit extends Cubit<PaymentProcessingState> {
: super(PaymentProcessingStateNotStarted());
Future<void> createSession(int price, String confirmationId,
- bool applyClubPoints) async {
+ bool applyClubPoints, String hotelCode) async {
try {
emit(PaymentProcessingStateProcessing());
final paymentConfigurations = await preregistrationRepository
- .sessionCheckout(confirmationId, applyClubPoints);
+ .sessionCheckout(hotelCode, confirmationId, applyClubPoints);
if (paymentConfigurations == null) {
emit(PaymentProcessingStateConfirmed());
} else {
diff --git a/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart b/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart
index e6926ef5..f48ad7dd 100644
--- a/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart
+++ b/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart
@@ -9,6 +9,7 @@ import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/services/adyen/payment_event_handler.dart';
import 'package:comwell_key_app/services/adyen/payment_method.dart';
+import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:flutter/material.dart';
@@ -23,18 +24,29 @@ class PaymentCardsCubit extends Cubit<PaymentCardsState> {
final ProfileRepository profileRepository = locator<ProfileRepository>();
final Api _api = Api();
late PaymentConfigurations? paymentConfigurations;
-
- PaymentCardsCubit() : super(PaymentCardsState.initial()) {
+ void Function(StoredPaymentMethod)? onPaymentMethodSelected;
+ void Function()? onPaymentMethodValidationError;
+
+ PaymentCardsCubit({
+ this.onPaymentMethodSelected,
+ this.onPaymentMethodValidationError,
+ StoredPaymentMethod? initialSelectedPaymentMethod,
+ }) : super(PaymentCardsState.initial()) {
+ if (initialSelectedPaymentMethod != null) {
+ emit(state.paymentMethodSelected(initialSelectedPaymentMethod));
+ }
init();
}
+ bool get isSelectedPaymentMethod => state.selectedPaymentMethod != null;
+
Future<PaymentConfigurations?> fetchPaymentConfiguration() async {
try {
- final session =
- await _preregistrationRepository.fetchSessionForAddingCard();
+ final session =
+ await _preregistrationRepository.fetchSessionForAddingCard();
- if (session == null) {
- return null;
+ if (session == null) {
+ return null;
}
return session;
} catch (e) {
@@ -63,8 +75,7 @@ class PaymentCardsCubit extends Cubit<PaymentCardsState> {
PaymentResult result, BuildContext context) async {
switch (result) {
case PaymentAdvancedFinished():
- final sessionData = result as PaymentAdvancedFinished;
- debugPrint("Result advanced: ${sessionData.resultCode}");
+ debugPrint("Result advanced: ${result.resultCode}");
if (context.mounted) {
context.pop();
@@ -72,8 +83,7 @@ class PaymentCardsCubit extends Cubit<PaymentCardsState> {
}
break;
case PaymentSessionFinished():
- final sessionData = result as PaymentSessionFinished;
- debugPrint("Result session: ${sessionData.sessionData}");
+ debugPrint("Result session: ${result.sessionData}");
if (context.mounted) {
context.pop();
init();
@@ -101,16 +111,17 @@ class PaymentCardsCubit extends Cubit<PaymentCardsState> {
}
SessionCheckout sessionCheckout() {
- return paymentConfigurations?.sessionCheckout ?? SessionCheckout(
- id: "123",
- sessionData: "123",
- paymentMethods: {
- "scheme": {
- "type": "scheme",
- "brands": ["visa", "mc"],
- },
- },
- );
+ return paymentConfigurations?.sessionCheckout ??
+ SessionCheckout(
+ id: "123",
+ sessionData: "123",
+ paymentMethods: {
+ "scheme": {
+ "type": "scheme",
+ "brands": ["visa", "mc"],
+ },
+ },
+ );
}
Checkout advancedCheckout() {
@@ -121,7 +132,8 @@ class PaymentCardsCubit extends Cubit<PaymentCardsState> {
try {
final user = await profileRepository.fetchProfileSettings();
final shopperRef = user.id.toString();
- final merchantAccount = paymentConfigurations?.sessionCheckout.id as String;
+ final merchantAccount =
+ paymentConfigurations?.sessionCheckout.id as String;
final response =
await _api.submitPayment(data, shopperRef, merchantAccount);
return PaymentEventHandler().handleResponse(jsonResponse: response);
@@ -150,9 +162,27 @@ class PaymentCardsCubit extends Cubit<PaymentCardsState> {
try {
emit(state.loading(true));
await _paymentCardsRepository.removeCard(cardId);
+ // If the removed card was selected, clear the selection
+ if (state.selectedPaymentMethod?.id == cardId) {
+ emit(state.clearSelectedPaymentMethod());
+ }
init();
} catch (e) {
debugPrint("error: $e");
}
}
+
+ void selectPaymentMethod(StoredPaymentMethod paymentMethod) {
+ emit(state.paymentMethodSelected(paymentMethod).setMissingPaymentMethod(false));
+ onPaymentMethodSelected?.call(paymentMethod);
+ }
+
+ void setMissingPaymentMethodError() {
+ emit(state.setMissingPaymentMethod(true));
+ onPaymentMethodValidationError?.call();
+ }
+
+ void clearMissingPaymentMethodError() {
+ emit(state.setMissingPaymentMethod(false));
+ }
}
diff --git a/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart b/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart
index 62d08191..922fdb45 100644
--- a/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart
+++ b/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart
@@ -5,17 +5,23 @@ class PaymentCardsState extends Equatable {
final bool isLoading;
final bool hasError;
final Iterable<StoredPaymentMethod> cards;
+ final StoredPaymentMethod? selectedPaymentMethod;
+ final bool missingPaymentMethod;
const PaymentCardsState._({
required this.cards,
required this.isLoading,
required this.hasError,
+ this.selectedPaymentMethod,
+ this.missingPaymentMethod = false,
});
PaymentCardsState.initial()
: cards = [],
isLoading = true,
- hasError = false;
+ hasError = false,
+ selectedPaymentMethod = null,
+ missingPaymentMethod = false;
PaymentCardsState cardsFetched(Iterable<StoredPaymentMethod> cards) {
return _copyWith(cards: cards, isLoading: false);
@@ -37,15 +43,31 @@ class PaymentCardsState extends Equatable {
bool? isLoading,
bool? hasError,
bool? hasCards,
+ StoredPaymentMethod? selectedPaymentMethod,
+ bool? missingPaymentMethod,
}) {
return PaymentCardsState._(
cards: cards ?? this.cards,
isLoading: isLoading ?? this.isLoading,
hasError: hasError ?? this.hasError,
+ selectedPaymentMethod: selectedPaymentMethod ?? this.selectedPaymentMethod,
+ missingPaymentMethod: missingPaymentMethod ?? this.missingPaymentMethod,
);
}
+ PaymentCardsState paymentMethodSelected(StoredPaymentMethod? paymentMethod) {
+ return _copyWith(selectedPaymentMethod: paymentMethod);
+ }
+
+ PaymentCardsState clearSelectedPaymentMethod() {
+ return _copyWith(selectedPaymentMethod: null);
+ }
+
+ PaymentCardsState setMissingPaymentMethod(bool value) {
+ return _copyWith(missingPaymentMethod: value);
+ }
+
@override
- List<Object?> get props => [isLoading, hasError, cards];
+ List<Object?> get props => [isLoading, hasError, cards, selectedPaymentMethod, missingPaymentMethod];
}
diff --git a/comwell_key_app/lib/payment_cards/payment_cards_page.dart b/comwell_key_app/lib/payment_cards/payment_cards_page.dart
index 7bc78917..ccd49527 100644
--- a/comwell_key_app/lib/payment_cards/payment_cards_page.dart
+++ b/comwell_key_app/lib/payment_cards/payment_cards_page.dart
@@ -4,10 +4,11 @@ import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
import 'package:comwell_key_app/payment_cards/components/add_card.dart';
import 'package:comwell_key_app/payment_cards/components/edit_card_dialog.dart';
import 'package:comwell_key_app/pregistration/components/card_item.dart';
+import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
+import 'package:comwell_key_app/themes/comwell_colors.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
-import '../themes/light_theme.dart';
import 'bloc/payment_cards_cubit.dart';
import '../common/components/shimmer_loader/payment_cards_shimmer_loader.dart';
@@ -17,7 +18,6 @@ class PaymentCardsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final theme = Theme.of(context);
if (needScaffold) {
return Scaffold(
backgroundColor: colorBackground,
@@ -53,11 +53,16 @@ class PaymentCardsPage extends StatelessWidget {
return const Center(child: PaymentCardsShimmerLoader());
}
if (cubit.state.hasError) {
- return Center(
- child: Text(
- 'error_cards'.tr(),
- style: theme.textTheme.bodyMedium,
- ),
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ComwellErrorWidget(
+ title: 'generic_error_title'.tr(),
+ subtitle: 'payment_cards_error_subtitle'.tr(),
+ border: false,
+ ),
+ ],
);
}
return _buildPaymentCards(context, state, cubit);
@@ -70,7 +75,19 @@ class PaymentCardsPage extends StatelessWidget {
BuildContext context, PaymentCardsState state, PaymentCardsCubit cubit) {
final cards = state.cards;
final theme = Theme.of(context);
-
+ final showPaymentError =
+ state.missingPaymentMethod && state.selectedPaymentMethod == null;
+
+ return _buildCardsList(context, theme, cards, cubit, showPaymentError);
+ }
+
+ Widget _buildCardsList(
+ BuildContext context,
+ ThemeData theme,
+ Iterable<StoredPaymentMethod> cards,
+ PaymentCardsCubit cubit,
+ bool showPaymentError,
+ ) {
return ListView(
children: [
Padding(
@@ -87,33 +104,67 @@ class PaymentCardsPage extends StatelessWidget {
Text("payment_cards_my_cards".tr(),
style: theme.textTheme.headlineMedium),
const SizedBox(height: 20),
+ if (showPaymentError) ...[
+ Container(
+ padding: const EdgeInsets.all(12),
+ margin: const EdgeInsets.only(bottom: 12),
+ decoration: BoxDecoration(
+ color: colorError.withValues(alpha: 0.1),
+ border: Border.all(color: colorError),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Row(
+ children: [
+ const Icon(Icons.error_outline,
+ color: colorError, size: 20),
+ const SizedBox(width: 8),
+ Expanded(
+ child: Text(
+ "payment_cards_missing_payment_method_subtitle".tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: colorError,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
...cards.map(
- (card) => Padding(
- padding: const EdgeInsets.symmetric(vertical: 4.0),
- child: Container(
- decoration: BoxDecoration(
- border: Border.all(color: colorDivider),
- ),
- padding: const EdgeInsets.all(12),
- child: CardItem(
- paymentMethod: card,
- onClick: () {
- showModalBottomSheet<void>(
- context: context,
- backgroundColor: colorBackground,
- builder: (context) {
- return Padding(
- padding: const EdgeInsets.all(16.0),
- child: EditCardDialog(
- storedPaymentMethod: card,
- onRemoveCard: () {
- cubit.onRemoveCard(card.id);
- },
- ),
- );
- },
+ (card) => InkWell(
+ onTap: () {
+ showModalBottomSheet<void>(
+ context: context,
+ backgroundColor: colorBackground,
+ builder: (context) {
+ return Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: EditCardDialog(
+ storedPaymentMethod: card,
+ onRemoveCard: () {
+ cubit.onRemoveCard(card.id);
+ },
+ ),
);
},
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 4.0),
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(color: colorDivider),
+ ),
+ padding: const EdgeInsets.all(12),
+ child: CardItem(
+ needsScaffold: needScaffold,
+ paymentMethod: card,
+ onSelect: () {
+ cubit.selectPaymentMethod(card);
+ },
+ isSelectedPaymentMethod:
+ cubit.state.selectedPaymentMethod?.id == card.id,
+ ),
),
),
),
diff --git a/comwell_key_app/lib/pregistration/components/card_item.dart b/comwell_key_app/lib/pregistration/components/card_item.dart
index 02096a32..2df8f357 100644
--- a/comwell_key_app/lib/pregistration/components/card_item.dart
+++ b/comwell_key_app/lib/pregistration/components/card_item.dart
@@ -7,64 +7,78 @@ import 'package:flutter_svg/flutter_svg.dart';
class CardItem extends StatelessWidget {
final StoredPaymentMethod paymentMethod;
- final bool isSelected;
- final VoidCallback? onClick;
+ final bool isSelectedPaymentMethod;
+ final VoidCallback? onSelect;
+ final bool needsScaffold;
const CardItem({
super.key,
required this.paymentMethod,
- this.onClick,
- this.isSelected = false,
+ required this.isSelectedPaymentMethod,
+ this.onSelect,
+ this.needsScaffold = false,
});
@override
Widget build(BuildContext context) {
- return InkWell(
- onTap: onClick,
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- PaymentCardImage(cardType: CardType.fromString(paymentMethod.brand)),
- const SizedBox(width: 12),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- paymentMethod.holderName,
- style: Theme.of(context).textTheme.bodySmall,
- ),
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- ..."**** **** **** ".characters.map((char) {
- if (char == '*') {
- return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 1.0),
- child: Container(
- width: 5,
- height: 5,
- decoration: const BoxDecoration(
- shape: BoxShape.circle, color: colorTertiary),
- ),
- );
- }
- return const SizedBox(width: 8);
- }),
- Text(
- paymentMethod.lastFour,
- style: Theme.of(context).textTheme.bodyMedium,
+ return Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ PaymentCardImage(cardType: CardType.fromString(paymentMethod.brand)),
+ const SizedBox(width: 12),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ paymentMethod.holderName,
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ ..."**** **** **** ".characters.map((char) {
+ if (char == '*') {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 1.0),
+ child: Container(
+ width: 5,
+ height: 5,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle, color: colorTertiary),
+ ),
+ );
+ }
+ return const SizedBox(width: 8);
+ }),
+ Text(
+ paymentMethod.lastFour,
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ ],
+ ),
+ ],
+ ),
+ const Expanded(child: SizedBox()),
+ !needsScaffold ? InkWell(
+ customBorder: const CircleBorder(),
+ radius: 18,
+ onTap: onSelect,
+ child: isSelectedPaymentMethod
+ ? SvgPicture.asset("assets/icons/ic_checkmark.svg", width: 24, height: 24)
+ : Container(
+ width: 24,
+ height: 24,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(
+ color: colorTertiary,
+ width: 1.0,
+ ),
),
- ],
- ),
- ],
- ),
- const Expanded(child: SizedBox()),
- isSelected
- ? SvgPicture.asset("assets/icons/ic_checkmark.svg")
- : const Icon(Icons.chevron_right, size: 36)
- ],
- ),
+ ),
+ ) : const Icon(Icons.arrow_forward_ios, color: colorTertiary, size: 24),
+ ],
);
}
}
diff --git a/comwell_key_app/lib/pregistration/components/clickable_card_item.dart b/comwell_key_app/lib/pregistration/components/clickable_card_item.dart
deleted file mode 100644
index 6e6663ce..00000000
--- a/comwell_key_app/lib/pregistration/components/clickable_card_item.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-import 'package:comwell_key_app/pregistration/components/card_item.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:flutter/material.dart';
-
-class ClickableCardItem extends StatelessWidget {
- final StoredPaymentMethod paymentMethod;
- final VoidCallback onClick;
- final bool isSelected;
-
- const ClickableCardItem({
- super.key,
- required this.paymentMethod,
- required this.onClick,
- this.isSelected = false,
- });
-
- @override
- Widget build(BuildContext context) {
- return InkWell(
- onTap: onClick,
- child: Container(
- decoration: BoxDecoration(
- border: Border.all(color: colorDivider),
- ),
- padding: const EdgeInsets.all(12),
- child: CardItem(paymentMethod: paymentMethod, isSelected: isSelected),
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/pregistration/components/information_card.dart b/comwell_key_app/lib/pregistration/components/information_card.dart
index 32720d52..6e9b558a 100644
--- a/comwell_key_app/lib/pregistration/components/information_card.dart
+++ b/comwell_key_app/lib/pregistration/components/information_card.dart
@@ -33,7 +33,7 @@ class InformationCard extends StatelessWidget {
TextButton(onPressed: onEditClick, child: Text("edit".tr(), style: const TextStyle(decoration: TextDecoration.underline, decorationColor: sandColor, color: sandColor),))
],
),
- const SizedBox(height: 40),
+ const SizedBox(height: 10),
child
],
),
diff --git a/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart b/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart
index 842f2bf3..280234f8 100644
--- a/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart
+++ b/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart
@@ -1,24 +1,26 @@
import 'package:bloc/bloc.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_state.dart';
+import 'package:comwell_key_app/pregistration/prereg_request_model.dart';
import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
import 'package:comwell_key_app/pregistration/utils/utils.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile_settings/model/address.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
+import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/tracking/models/analytics_event_item.dart';
import 'package:comwell_key_app/up_sales/models/addon_list.dart';
import 'package:comwell_key_app/up_sales/models/addon_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/room_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/upgrade.dart';
-import 'package:comwell_key_app/up_sales/models/up_sales.dart';
import 'package:comwell_key_app/up_sales/up_sales_repository.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/phone_utils.dart';
import 'package:country_code_picker/country_code_picker.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
class PreregistrationCubit extends Cubit<PreregistrationState> {
@@ -29,7 +31,6 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
final _upSalesRepository = locator<UpSalesRepository>();
final Booking booking;
- final UpSales upSales;
final pageController = PageController();
final addressTextController = TextEditingController();
@@ -39,17 +40,25 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
final lastNameTextController = TextEditingController();
final emailTextController = TextEditingController();
final phoneNumberTextController = TextEditingController();
+ final documentNumberTextController = TextEditingController();
CountryCode? countryCode;
String? phoneNumber;
- String selectedCountry = '';
+ final List<String> favoriteCountries = ['DK', 'SE', 'NO', 'FI'];
+ final List<String> documentTypes = [
+ 'document_type_passport'.tr(),
+ 'document_type_id_card'.tr(),
+ 'document_type_driver_license'.tr(),
+ 'document_type_other'.tr()
+ ];
+ String selectedDocumentType = '';
PreregistrationPage get currentPage =>
PreregistrationPage.fromIndex(pageController.page?.toInt() ?? 0);
bool _isAnimating = false;
- PreregistrationCubit({required this.booking, required this.upSales})
+ PreregistrationCubit({required this.booking})
: super(const PreregistrationState(
isLoading: false,
selected: false,
@@ -68,7 +77,8 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
isLastNameValid: false,
isAddressValid: false,
isPostalCodeValid: false,
- isCityValid: false)) {
+ isCityValid: false,
+ selectedCountry: '')) {
_tracking.trackScreenView(
"Pre-registration - Betalingskort",
"/pre-registration/betalingskort",
@@ -110,7 +120,6 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
addressTextController.text = user.address.street;
postalCodeTextController.text = user.address.zipCode;
cityTextController.text = user.address.city;
- selectedCountry = user.address.country;
countryCode = getCountryCodeFromPhoneNumber(user.phoneNumber).$1;
phoneNumber = getCountryCodeFromPhoneNumber(user.phoneNumber).$2;
@@ -118,15 +127,28 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
firstNameTextController.text = user.firstName;
lastNameTextController.text = user.lastName;
emailTextController.text = user.email;
- phoneNumberTextController.text = user.phoneNumber;
-
- emit(state.loaded(upSales: upSales, user: user));
+ phoneNumberTextController.text = phoneNumber ?? '';
+
+ final upSales = await _upSalesRepository.getUpSales(
+ booking.confirmationNumber, booking.hotelCode);
+ emit(state
+ .loaded(user: user, upSales: upSales)
+ .copyWith(
+ selectedCountry: countryCode?.code ?? 'DK',
+ countryCode: countryCode,
+ phoneNumber: phoneNumber,
+ ));
} on Exception catch (e) {
debugPrint("error fetching preregistration: $e");
emit(state.copyWith(isLoading: false));
}
}
+ void onDocumentTypeSelected(String documentType) {
+ selectedDocumentType = documentType;
+ emit(state.copyWith(selectedDocumentType: documentType));
+ }
+
void onAddressContinueClicked() {
if (isAddressValid && isCityValid && isPostalCodeValid) {
final updatedUser = state.user!.copyWith(
@@ -134,7 +156,7 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
street: addressTextController.text,
zipCode: postalCodeTextController.text,
city: cityTextController.text,
- country: selectedCountry,
+ country: state.selectedCountry,
));
emit(state.copyWith(user: updatedUser));
@@ -160,8 +182,17 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
}
}
- void onPaymentContinueClicked() {
- _navigateNextPage();
+ bool onPaymentContinueClicked() {
+ if (state.selectedPaymentMethod != null) {
+ _navigateNextPage();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void onPaymentMethodValidationError() {
+ emit(state.copyWith(missingInformation: true));
}
void onUpSalesContinueClicked() {
@@ -177,9 +208,10 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
case PreregistrationPage.address:
onAddressContinueClicked();
break;
- // case PreregistrationPage.payment:
- // onPaymentContinueClicked();
- // break;
+ case PreregistrationPage.payment:
+ // Payment validation is handled in preregistration_flow.dart
+ onPaymentContinueClicked();
+ break;
case PreregistrationPage.upSales:
onUpSalesContinueClicked();
break;
@@ -189,6 +221,13 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
}
}
+ void onPaymentMethodSelected(StoredPaymentMethod paymentMethod) {
+ emit(state.copyWith(
+ selectedPaymentMethod: paymentMethod,
+ missingInformation: false,
+ ));
+ }
+
void addToCart() {
final analyticsEventItem = AnalyticsEventItem(
hotelName: "Comwell",
@@ -220,7 +259,6 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
void _onConfirmPressed(BuildContext context) async {
emit(state.copyWith(isLoading: true));
try {
- final confirmationId = booking.confirmationId;
final hasUpSales = state.addOnUpgrades.any((e) => e.isAddedToCart) ||
state.selectedRoomUpgrade.isNotEmpty;
@@ -228,8 +266,26 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
if (hasUpSales) {
addUpSalesToBooking();
}
+
+ // Build the PreregRequestModel with all required fields
+ final preregRequest = PreregRequestDto(
+ firstName: state.user!.firstName,
+ lastName: state.user!.lastName,
+ email: state.user!.email,
+ phoneNumber: state.user!.phoneNumber,
+ birthDay: state.user!.birthDate.toIso8601String(),
+ address: state.user!.address.street,
+ zipCode: state.user!.address.zipCode,
+ city: state.user!.address.city,
+ country: state.user!.address.country,
+ confirmationNumber: booking.confirmationNumber,
+ hotelCode: booking.hotelCode,
+ comwellClubTermsOfUseConsentChoice: state.termsAndConditionsAccepted,
+ comwellClubMarketingConsentChoice: false,
+ );
+
final preRegResponse = await _preregistrationRepository
- .createPreregistration(confirmationId);
+ .createPreregistration(preregRequest);
if (preRegResponse != null) {
Future.delayed(const Duration(seconds: 3), () {
@@ -239,12 +295,12 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
});
} else {
emit(state.setError(error: Error()));
- if (!context.mounted) return;
+ if (!context.mounted) return;
context.pop(null);
}
} catch (e) {
emit(state.setError(error: Error()));
- }
+ }
}
Future<void> addUpSalesToBooking() async {
@@ -258,7 +314,7 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
.toList();
await _upSalesRepository.addUpSalesToBooking(
- booking.confirmationId, booking.hotelCode, roomType, addonList);
+ booking.confirmationNumber, booking.hotelCode, roomType, addonList);
}
void onEditProfileClicked() {
@@ -269,9 +325,9 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
_navigateTo(PreregistrationPage.address);
}
- // void onEditPaymentMethodCLickked() {
- // _navigateTo(PreregistrationPage.payment);
- // }
+ void onEditPaymentMethodCLickked() {
+ _navigateTo(PreregistrationPage.payment);
+ }
void onEditExtrasClicked() {
_navigateTo(PreregistrationPage.upSales);
@@ -282,8 +338,9 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
emit(state.copyWith(isPhoneNumberValid: isPhoneNumberValid));
}
- void onCountryCodeSelected(CountryCode countryCode) {
- countryCode = countryCode;
+ void onCountryCodeSelected(CountryCode country) {
+ countryCode = country;
+ emit(state.copyWith(countryCode: country, selectedCountry: country.code));
}
bool onBackClicked() {
@@ -333,7 +390,7 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
return selectedAddOnUpgrades.fold(
0, (sum, upgrade) => sum + upgrade.price * upgrade.quantity) +
(state.selectedRoomUpgrade.isNotEmpty
- ? upSales.roomUpgrades
+ ? state.availableRoomUpgrades
.firstWhere((e) => e.id == state.selectedRoomUpgrade)
.price
: 0);
@@ -365,6 +422,7 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
lastNameTextController.dispose();
emailTextController.dispose();
phoneNumberTextController.dispose();
+ documentNumberTextController.dispose();
return super.close();
}
@@ -374,8 +432,11 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
bool get isCityValid => cityTextController.text.isNotEmpty;
+ bool get isDocumentNumberValid =>
+ documentNumberTextController.text.isNotEmpty;
+
bool get isPhoneNumberValid =>
- phoneNumberTextController.text.length >= 8 &&
+ phoneNumberTextController.text.length >= 7 &&
phoneNumberTextController.text.length < 16;
bool get isFirstNameValid => firstNameTextController.text.isNotEmpty;
@@ -392,8 +453,8 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
return isFirstNameValid && isLastNameValid && isPhoneNumberValid;
case PreregistrationPage.address:
return isAddressValid && isPostalCodeValid && isCityValid;
- // case PreregistrationPage.payment:
- // return true;
+ case PreregistrationPage.payment:
+ return true;
case PreregistrationPage.upSales:
return true;
case PreregistrationPage.confirmation:
@@ -409,8 +470,8 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
return "generic_continue".tr();
case PreregistrationPage.address:
return "generic_continue".tr();
- // case PreregistrationPage.payment:
- // return "generic_continue".tr();
+ case PreregistrationPage.payment:
+ return "generic_continue".tr();
case PreregistrationPage.upSales:
if (selectedAddOnUpgrades.isEmpty &&
state.selectedRoomUpgrade.isEmpty) {
@@ -441,4 +502,17 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
void updateAddonUpgradeQuantity(AddOnUpgrade upgrade, int quantity) {
emit(state.updateSelectedUpgradeWithQuantity(upgrade, quantity));
}
+
+ String get paymentMethodImage {
+ switch (state.selectedPaymentMethod?.brand) {
+ case 'mastercard':
+ return 'assets/images/master.svg';
+ case 'visa':
+ return 'assets/images/visa.svg';
+ case 'maestro':
+ return 'assets/images/maestro.svg';
+ default:
+ return 'assets/images/visa.png';
+ }
+ }
}
diff --git a/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart b/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart
index 15af2aee..32ab38bb 100644
--- a/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart
+++ b/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart
@@ -1,3 +1,4 @@
+import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
import 'package:comwell_key_app/up_sales/models/addon_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/room_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/up_sales.dart';
@@ -16,6 +17,7 @@ class PreregistrationState extends Equatable {
final bool forceUpdate;
final User? user;
final CountryCode? countryCode;
+ final String selectedCountry;
final String? phoneNumber;
final bool missingInformation;
final int extrasTotalPrice;
@@ -26,6 +28,8 @@ class PreregistrationState extends Equatable {
final bool isAddressValid;
final bool isPostalCodeValid;
final bool isCityValid;
+ final String selectedDocumentType;
+ final StoredPaymentMethod? selectedPaymentMethod;
final Error? error;
const PreregistrationState({
@@ -41,13 +45,16 @@ class PreregistrationState extends Equatable {
this.user,
this.phoneNumber,
this.countryCode,
- this.isPhoneNumberValid = false,
+ this.selectedCountry = '',
+ this.isPhoneNumberValid = false,
this.isFirstNameValid = false,
this.isLastNameValid = false,
this.isAddressValid = false,
this.isPostalCodeValid = false,
this.isCityValid = false,
this.error,
+ this.selectedDocumentType = '',
+ this.selectedPaymentMethod,
});
PreregistrationState.initial()
@@ -63,13 +70,16 @@ class PreregistrationState extends Equatable {
user = null,
phoneNumber = null,
countryCode = null,
+ selectedCountry = '',
isPhoneNumberValid = false,
isFirstNameValid = false,
isLastNameValid = false,
isAddressValid = false,
isPostalCodeValid = false,
isCityValid = false,
- error = null;
+ error = null,
+ selectedDocumentType = '',
+ selectedPaymentMethod = null;
PreregistrationState loaded({required UpSales upSales, required User user}) =>
copyWith(
@@ -128,10 +138,13 @@ class PreregistrationState extends Equatable {
User? user,
String? phoneNumber,
CountryCode? countryCode,
+ String? selectedCountry,
+ String? selectedDocumentType,
bool? isPhoneNumberValid,
bool? isFirstNameValid,
bool? isLastNameValid,
bool? isAddressValid,
+ StoredPaymentMethod? selectedPaymentMethod,
bool? isPostalCodeValid,
bool? isCityValid,
Error? error,
@@ -150,6 +163,9 @@ class PreregistrationState extends Equatable {
forceUpdate: forceUpdate ?? this.forceUpdate,
user: user ?? this.user,
error: error ?? this.error,
+ selectedCountry: selectedCountry ?? this.selectedCountry,
+ selectedDocumentType: selectedDocumentType ?? this.selectedDocumentType,
+ selectedPaymentMethod: selectedPaymentMethod ?? this.selectedPaymentMethod,
);
}
@@ -163,6 +179,7 @@ class PreregistrationState extends Equatable {
user,
phoneNumber,
countryCode,
+ selectedCountry,
missingInformation,
extrasTotalPrice,
termsAndConditionsAccepted,
@@ -175,6 +192,8 @@ class PreregistrationState extends Equatable {
isCityValid,
availableRoomUpgrades,
selectedRoomUpgrade,
+ selectedDocumentType,
+ selectedPaymentMethod,
error,
];
}
diff --git a/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart b/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart
index ad75b071..25682f14 100644
--- a/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart
+++ b/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart
@@ -16,11 +16,13 @@ class PreregAddressPage extends StatelessWidget {
return BlocBuilder<PreregistrationCubit, PreregistrationState>(
builder: (context, state) {
final cubit = context.read<PreregistrationCubit>();
-
+
if (state.isLoading) {
return const Center(child: PreregAddressShimmerLoader());
}
+ final isFavoriteCountry = cubit.favoriteCountries.contains(state.selectedCountry);
+
final addressErrorMessage =
!cubit.isAddressValid && state.missingInformation
? "generic_information_required".tr()
@@ -32,6 +34,12 @@ class PreregAddressPage extends StatelessWidget {
final cityErrorMessage = !cubit.isCityValid && state.missingInformation
? "generic_information_required".tr()
: null;
+
+ final documentNumberErrorMessage =
+ !cubit.isDocumentNumberValid && state.missingInformation
+ ? "generic_information_required".tr()
+ : null;
+
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
key: const PageStorageKey("information_form"),
@@ -78,18 +86,61 @@ class PreregAddressPage extends StatelessWidget {
textOverflow: TextOverflow.visible,
initialSelection: cubit.countryCode?.name,
showFlag: true,
- favorite: const ['DK', 'SE', 'NO', 'DE'],
+ favorite: cubit.favoriteCountries,
showDropDownButton: true,
- padding: const EdgeInsets.symmetric(horizontal: 16),
textStyle: Theme.of(context).textTheme.headlineSmall,
- showCountryOnly: false,
+ showCountryOnly: true,
showOnlyCountryWhenClosed: true,
onChanged: (CountryCode countryCode) {
- cubit.selectedCountry = countryCode.name ?? '';
+ cubit.onCountryCodeSelected(countryCode);
},
),
),
- const SizedBox(height: 100),
+ const SizedBox(height: 12),
+ if (!isFavoriteCountry) ...[
+ const Divider(color: colorDivider),
+ const SizedBox(height: 12),
+ Container(
+ height: 62,
+ width: double.infinity,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: colorDivider),
+ ),
+ child: Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
+ child: DropdownButtonFormField<String>(
+ hint: Text("document_type".tr()),
+ initialValue: state.selectedDocumentType.isNotEmpty
+ ? state.selectedDocumentType
+ : null,
+ style: Theme.of(context)
+ .textTheme
+ .headlineSmall
+ ?.copyWith(color: Colors.black),
+ items: cubit.documentTypes
+ .map((documentType) => DropdownMenuItem(
+ value: documentType, child: Text(documentType)))
+ .toList(),
+ onChanged: (value) {
+ if (value != null) {
+ cubit.onDocumentTypeSelected(value);
+ }
+ },
+ ),
+ ),
+ ),
+ const SizedBox(height: 12),
+ ComwellTextField(
+ key: const Key("document_number"),
+ fieldName: "document_number".tr(),
+ textInputType: TextInputType.number,
+ initialValue: "",
+ readOnly: false,
+ errorMessage: documentNumberErrorMessage,
+ controller: cubit.documentNumberTextController),
+ ],
],
);
});
diff --git a/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart b/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart
index 5ada5642..77558720 100644
--- a/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart
+++ b/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart
@@ -1,147 +1,177 @@
+import 'package:comwell_key_app/payment_cards/bloc/payment_cards_cubit.dart';
+import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_cubit.dart';
import 'package:comwell_key_app/pregistration/components/information_card.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_svg/svg.dart';
class PreregConfirmationPage extends StatelessWidget {
const PreregConfirmationPage({super.key});
@override
Widget build(BuildContext context) {
- final cubit = context.read<PreregistrationCubit>();
- final state = cubit.state;
- final user = state.user!;
- final theme = Theme.of(context);
- final singularExtra = cubit.numOfExtras == 1 &&
- cubit.state.selectedRoomUpgrade.isEmpty ||
- cubit.numOfExtras == 0 &&
- cubit.state.selectedRoomUpgrade.isNotEmpty;
- String extrasTitleText;
-
- if (singularExtra) {
- extrasTitleText =
- "preregistration_confirmation_extras_card_title_singular".tr();
- } else {
- extrasTitleText = "preregistration_confirmation_extras_card_title_plural"
- .tr(args: [cubit.numOfExtras.toString()]);
- }
+ return BlocBuilder<PaymentCardsCubit, PaymentCardsState>(
+ builder: (context, state) {
+ final cubit = context.read<PreregistrationCubit>();
- return SafeArea(
- child: ListView(
- padding: const EdgeInsets.symmetric(horizontal: 12.0),
- children: [
- const SizedBox(height: 16),
- Text("preregistration_confirmation_title".tr(),
- style: Theme.of(context).textTheme.headlineLarge),
- const SizedBox(height: 16),
- InformationCard(
- title: "preregistration_confirmation_profile_card_title".tr(),
- titleStyle: theme.textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.w500,
- color: colorHeadlineText,
- ),
- onEditClick: cubit.onEditProfileClicked,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${state.user!.firstName} ${state.user!.lastName}",
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- Text(
- user.email,
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- Text(
- state.user!.phoneNumber,
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- ],
- ),
- ),
- const SizedBox(height: 12),
- InformationCard(
- title: "preregistration_confirmation_address_card_title".tr(),
- titleStyle: theme.textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.w500,
- color: colorHeadlineText,
+ final state = cubit.state;
+ final user = state.user!;
+ final theme = Theme.of(context);
+ final singularExtra = cubit.numOfExtras == 1 &&
+ cubit.state.selectedRoomUpgrade.isEmpty ||
+ cubit.numOfExtras == 0 && cubit.state.selectedRoomUpgrade.isNotEmpty;
+ String extrasTitleText;
+
+
+ if (singularExtra) {
+ extrasTitleText =
+ "preregistration_confirmation_extras_card_title_singular".tr();
+ } else {
+ extrasTitleText =
+ "preregistration_confirmation_extras_card_title_plural"
+ .tr(args: [cubit.numOfExtras.toString()]);
+ }
+
+ return SafeArea(
+ child: ListView(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0),
+ children: [
+ const SizedBox(height: 16),
+ Text("preregistration_confirmation_title".tr(),
+ style: Theme.of(context).textTheme.headlineLarge),
+ const SizedBox(height: 16),
+ InformationCard(
+ title: "preregistration_confirmation_profile_card_title".tr(),
+ titleStyle: theme.textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.w500,
+ color: colorHeadlineText,
+ ),
+ onEditClick: cubit.onEditProfileClicked,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "${state.user!.firstName} ${state.user!.lastName}",
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ Text(
+ user.email,
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ Text(
+ state.user!.phoneNumber,
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ ],
+ ),
),
- onEditClick: cubit.onEditAddressClicked,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- state.user!.address.street,
- style: theme.textTheme.bodyMedium,
- ),
- Text(
- "${state.user!.address.zipCode}, ${state.user!.address.city}${state.user!.address.country.isNotEmpty ? ', ${state.user!.address.country}' : ''}",
- style: theme.textTheme.bodyMedium,
- ),
- ],
+ const SizedBox(height: 12),
+ InformationCard(
+ title: "preregistration_confirmation_address_card_title".tr(),
+ titleStyle: theme.textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.w500,
+ color: colorHeadlineText,
+ ),
+ onEditClick: cubit.onEditAddressClicked,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ cubit.state.user!.address.street,
+ style: theme.textTheme.bodyMedium,
+ ),
+ Text(
+ "${state.user!.address.zipCode}, ${state.user!.address.city}${state.user!.address.country.isNotEmpty ? ', ${state.user!.address.country}' : ''}",
+ style: theme.textTheme.bodyMedium,
+ ),
+ ],
+ ),
),
- ),
- const SizedBox(height: 12),
- InformationCard(
- title: "payment_card_profile_menu".tr(),
- titleStyle: theme.textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.w500,
- color: colorHeadlineText,
+ const SizedBox(height: 12),
+ InformationCard(
+ title: "payment_card_profile_menu".tr(),
+ titleStyle: theme.textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.w500,
+ color: colorHeadlineText,
+ ),
+ onEditClick: cubit.onEditPaymentMethodCLickked,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ SvgPicture.asset(
+ cubit.paymentMethodImage,
+ height: 30),
+ const SizedBox(width: 12),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ cubit.state.selectedPaymentMethod?.holderName ??
+ '',
+ style: theme.textTheme.bodyMedium),
+ Row(
+ spacing: 4,
+ children: [
+ ...'•••• •••• •••• '.characters.map((char) {
+ if (char == ' ') {
+ return const SizedBox(width: 4);
+ }
+ return Container(
+ width: 5,
+ height: 5,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle,
+ color: colorTertiary,
+ ),
+ );
+ }),
+ Text(
+ cubit.state.selectedPaymentMethod?.lastFour ?? '',
+ style: theme.textTheme.bodyMedium,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
),
- onEditClick: cubit.onPaymentContinueClicked,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Image.asset('assets/images/master.png',
- width: 48, height: 48),
- const SizedBox(width: 12),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("${user.firstName} ${user.lastName}",
- style: theme.textTheme.bodyMedium),
- Text('•••• •••• •••• 1234',
- style: theme.textTheme.bodyMedium),
- const SizedBox(width: 8),
- ],
+ const SizedBox(height: 12),
+ InformationCard(
+ title: extrasTitleText,
+ titleStyle: theme.textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.w500,
+ color: colorHeadlineText,
+ ),
+ onEditClick: cubit.onEditExtrasClicked,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "total_charge_value"
+ .tr(args: [cubit.extrasTotalPrice.toString()]),
+ style: theme.textTheme.bodyMedium),
+ Text(
+ "preregistration_confirmation_extras_card_subtitle".tr(),
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: colorHeadlineText,
),
- ],
- ),
- ],
- ),
- ),
- const SizedBox(height: 12),
- InformationCard(
- title: extrasTitleText,
- titleStyle: theme.textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.w500,
- color: colorHeadlineText,
- ),
- onEditClick: cubit.onEditExtrasClicked,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "total_charge_value"
- .tr(args: [cubit.extrasTotalPrice.toString()]),
- style: theme.textTheme.bodyMedium),
- Text(
- "preregistration_confirmation_extras_card_subtitle".tr(),
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText,
- ),
- )
- ],
+ )
+ ],
+ ),
),
- ),
- const SizedBox(height: 40),
- ],
- ),
- );
+ const SizedBox(height: 40),
+ ],
+ ),
+ );
+ });
}
}
diff --git a/comwell_key_app/lib/pregistration/pages/prereg_up_sales_catalog_page.dart b/comwell_key_app/lib/pregistration/pages/prereg_up_sales_catalog_page.dart
index 2090fda4..eac10787 100644
--- a/comwell_key_app/lib/pregistration/pages/prereg_up_sales_catalog_page.dart
+++ b/comwell_key_app/lib/pregistration/pages/prereg_up_sales_catalog_page.dart
@@ -1,3 +1,6 @@
+import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
+import 'package:comwell_key_app/common/components/comwell_error_widget.dart';
+import 'package:comwell_key_app/common/components/shimmer_loader/up_sales_catalog_shimmer_loader.dart';
import 'package:comwell_key_app/up_sales/components/catalog/addon_upgrade_catalog.dart';
import 'package:comwell_key_app/up_sales/components/catalog/room_upgrade_catalog.dart';
import 'package:comwell_key_app/up_sales/components/catalog/service_catalog.dart';
@@ -16,23 +19,26 @@ class PreregUpSalesCatalogPage extends StatelessWidget {
return BlocBuilder<PreregistrationCubit, PreregistrationState>(
builder: (context, state) {
final cubit = context.read<PreregistrationCubit>();
+
+ if (state.isLoading) {
+ return Scaffold(
+ appBar: const ComwellAppBar(),
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ body: const UpSalesCatalogShimmerLoader(),
+ );
+ }
+
final serviceUpgrades = cubit.state.addOnUpgrades
.where((upgrade) => upgrade.isService)
.toList();
- final addonUpgrades = cubit.upSales.addOnUpgrades
+ final addonUpgrades = cubit.state.addOnUpgrades
.where((upgrade) => !upgrade.isService)
.toList();
final selectedRoomUpgrade = cubit.state.selectedRoomUpgrade.isEmpty
? null
- : cubit.upSales.roomUpgrades
+ : cubit.state.availableRoomUpgrades
.firstWhere((e) => e.id == cubit.state.selectedRoomUpgrade);
- if (state.isLoading) {
- return const Center(
- child: CircularProgressIndicator(),
- );
- }
-
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
@@ -48,6 +54,12 @@ class PreregUpSalesCatalogPage extends StatelessWidget {
style: theme.textTheme.headlineLarge),
),
const SizedBox(height: 40),
+ if (serviceUpgrades.isEmpty && addonUpgrades.isEmpty && cubit.state.availableRoomUpgrades.isEmpty) ...[
+ ComwellErrorWidget(
+ title: 'up_sales_catalog_no_up_sales_title'.tr(),
+ subtitle: 'up_sales_catalog_no_up_sales_subtitle'.tr(),
+ border: true)
+ ],
if (serviceUpgrades.isNotEmpty) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
diff --git a/comwell_key_app/lib/pregistration/pregistration_repository.dart b/comwell_key_app/lib/pregistration/pregistration_repository.dart
index 519ba187..ed9bd0c9 100644
--- a/comwell_key_app/lib/pregistration/pregistration_repository.dart
+++ b/comwell_key_app/lib/pregistration/pregistration_repository.dart
@@ -1,11 +1,13 @@
import 'package:adyen_checkout/adyen_checkout.dart';
import 'package:comwell_key_app/check_out/models/payment_configurations.dart';
+import 'package:comwell_key_app/pregistration/prereg_request_model.dart';
import 'package:comwell_key_app/profile/utils/constants.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
class PreregistrationRepository {
final Api _api = Api();
@@ -15,10 +17,11 @@ class PreregistrationRepository {
PreregistrationRepository();
Future<PaymentConfigurations?> sessionCheckout(
+ String hotelCode,
String bookingId,
bool usePoints,
) async {
- final response = await _api.createAdyenSession(bookingId, usePoints);
+ final response = await _api.createAdyenSession(bookingId, usePoints, hotelCode);
final clientKey = response["clientKey"] as String;
final sessionResponse = response["sessionResponse"];
final payedWithPoints = response["isFullyPaidWithPoints"] as bool;
@@ -90,11 +93,14 @@ class PreregistrationRepository {
isFullyPaidWithPoints: false);
}
- Future<dynamic> createPreregistration(String confirmationId) async {
+ Future<dynamic> createPreregistration(PreregRequestDto request) async {
try {
- final response = await _api.preRegistration(confirmationId);
+ debugPrint("request: $request.toString()");
+ final response = await _api.preRegistration(request);
+
return response.data!;
} catch (e) {
+ debugPrint("error: $e");
return null;
}
}
@@ -195,8 +201,9 @@ Map<String, dynamic>? _findPaymentMethod(
for (final m in list) {
final t = (m['type'] as String?)?.toLowerCase();
- if (t == type.toLowerCase())
+ if (t == type.toLowerCase()) {
return m; // e.g. 'scheme', 'applepay', 'googlepay'
+ }
}
return null;
}
diff --git a/comwell_key_app/lib/pregistration/prereg_request_model.dart b/comwell_key_app/lib/pregistration/prereg_request_model.dart
new file mode 100644
index 00000000..087b01a3
--- /dev/null
+++ b/comwell_key_app/lib/pregistration/prereg_request_model.dart
@@ -0,0 +1,50 @@
+class PreregRequestDto {
+ final String firstName;
+ final String lastName;
+ final String email;
+ final String phoneNumber;
+ final String birthDay;
+ final String address;
+ final String zipCode;
+ final String city;
+ final String country;
+ final String confirmationNumber;
+
+ final String hotelCode;
+ final bool comwellClubTermsOfUseConsentChoice;
+ final bool comwellClubMarketingConsentChoice;
+
+ PreregRequestDto({
+ required this.firstName,
+ required this.lastName,
+ required this.email,
+ required this.phoneNumber,
+ required this.birthDay,
+ required this.address,
+ required this.zipCode,
+ required this.city,
+ required this.country,
+ required this.confirmationNumber,
+ required this.hotelCode,
+ required this.comwellClubTermsOfUseConsentChoice,
+ required this.comwellClubMarketingConsentChoice,
+ });
+
+ Map<String, dynamic> toJson() {
+ return {
+ 'firstName': firstName,
+ 'lastName': lastName,
+ 'email': email,
+ 'phoneNumber': phoneNumber,
+ 'birthDay': birthDay,
+ 'address': address,
+ 'zipCode': zipCode,
+ 'city': city,
+ 'country': country,
+ 'confirmationNumber': confirmationNumber,
+ 'hotelCode': hotelCode,
+ 'comwellClubTermsOfUseConsentChoice': comwellClubTermsOfUseConsentChoice,
+ 'comwellClubMarketingConsentChoice': comwellClubMarketingConsentChoice,
+ };
+ }
+}
diff --git a/comwell_key_app/lib/pregistration/preregistration_flow.dart b/comwell_key_app/lib/pregistration/preregistration_flow.dart
index e2abec63..a45a15b2 100644
--- a/comwell_key_app/lib/pregistration/preregistration_flow.dart
+++ b/comwell_key_app/lib/pregistration/preregistration_flow.dart
@@ -1,10 +1,13 @@
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
+import 'package:comwell_key_app/payment_cards/bloc/payment_cards_cubit.dart';
+import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_state.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_cubit.dart';
import 'package:comwell_key_app/pregistration/utils/utils.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
import '../common/components/shimmer_loader/prereg_flow_shimmer_loader.dart';
class PreregistrationFlow extends StatefulWidget {
@@ -17,65 +20,99 @@ class PreregistrationFlow extends StatefulWidget {
class _PreregistrationFlowState extends State<PreregistrationFlow> {
@override
Widget build(BuildContext context) {
- return BlocBuilder<PreregistrationCubit, PreregistrationState>(
- builder: (context, state) {
- final cubit = context.read<PreregistrationCubit>();
- return Scaffold(
- backgroundColor: Theme.of(context).colorScheme.surface,
- appBar: const ComwellAppBar(),
- body: GestureDetector(
- onTap: () {
- FocusManager.instance.primaryFocus?.unfocus();
+ return BlocProvider<PaymentCardsCubit>(
+ create: (context) => PaymentCardsCubit(
+ onPaymentMethodSelected: (paymentMethod) {
+ context
+ .read<PreregistrationCubit>()
+ .onPaymentMethodSelected(paymentMethod);
},
- child: Builder(
- builder: (context) {
- if (state.isLoading) {
- return const Center(child: PreregFlowShimmerLoader());
- }
- return PageView(
- key: const PageStorageKey("prereg_flow"),
- physics: const NeverScrollableScrollPhysics(),
- controller: cubit.pageController,
- children:
- PreregistrationPage.getPages(ValueKey(state)).toList(),
- );
- },
- ),
- ),
- bottomNavigationBar: Builder(builder: (context) {
- if (state.isLoading) return const SizedBox();
- return Column(
- // BOTTOM NAVIGATION
- mainAxisSize: MainAxisSize.min,
- children: [
- const Divider(
- color: colorDivider,
- height: 0,
+ onPaymentMethodValidationError: () {
+ context
+ .read<PreregistrationCubit>()
+ .onPaymentMethodValidationError();
+ },
+ initialSelectedPaymentMethod: context
+ .read<PreregistrationCubit>()
+ .state
+ .selectedPaymentMethod),
+ child: BlocBuilder<PaymentCardsCubit, PaymentCardsState>(
+ builder: (context, state) {
+ return BlocBuilder<PreregistrationCubit, PreregistrationState>(
+ builder: (context, state) {
+ final cubit = context.read<PreregistrationCubit>();
+ return Scaffold(
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ appBar: ComwellAppBar(
+ shouldShowProfileButton: false,
+ shouldShowBackButton: true,
+ onBackPressed: () {
+ if (cubit.currentPage == PreregistrationPage.profile) {
+ context.pop();
+ } else {
+ cubit.onBackClicked();
+ }
+ },
+ ),
+ body: Builder(
+ builder: (context) {
+ if (state.isLoading) {
+ return const Center(child: PreregFlowShimmerLoader());
+ }
+ return PageView(
+ key: const PageStorageKey("prereg_flow"),
+ physics: const NeverScrollableScrollPhysics(),
+ controller: cubit.pageController,
+ children: PreregistrationPage.getPages(ValueKey(state))
+ .toList(),
+ );
+ },
),
- Row(
- children: [
- Expanded(
- child: Padding(
- padding:
- const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 40.0),
- child: ElevatedButton(
- onPressed: () => cubit.onContinueClicked(context),
- style: ElevatedButton.styleFrom(
- foregroundColor: colorBackground),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 16.0),
- child: Text(cubit.buttonText),
+ bottomNavigationBar: Builder(builder: (context) {
+ if (state.isLoading) return const SizedBox();
+ return Column(
+ // BOTTOM NAVIGATION
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Divider(
+ color: Colors.black12,
+ height: 0,
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(
+ 16.0, 16.0, 16.0, 40.0),
+ child: ElevatedButton(
+ onPressed: () {
+ if (cubit.currentPage == PreregistrationPage.payment) {
+ final success = cubit.onPaymentContinueClicked();
+ if (!success) {
+ context.read<PaymentCardsCubit>().setMissingPaymentMethodError();
+ }
+ } else {
+ cubit.onContinueClicked(context);
+ }
+ },
+ style: ElevatedButton.styleFrom(
+ foregroundColor: colorBackground),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 16.0),
+ child: Text(cubit.buttonText),
+ ),
+ ),
+ ),
),
- ),
+ ],
),
- ),
- ],
- ),
- ],
- );
- }),
- );
- },
- );
+ ],
+ );
+ }),
+ );
+ },
+ );
+ }));
}
}
diff --git a/comwell_key_app/lib/pregistration/utils/utils.dart b/comwell_key_app/lib/pregistration/utils/utils.dart
index 5f5d3089..d28aa9c6 100644
--- a/comwell_key_app/lib/pregistration/utils/utils.dart
+++ b/comwell_key_app/lib/pregistration/utils/utils.dart
@@ -1,3 +1,4 @@
+import 'package:comwell_key_app/payment_cards/payment_cards_page.dart';
import 'package:comwell_key_app/pregistration/pages/prereg_address_page.dart';
import 'package:comwell_key_app/pregistration/pages/prereg_confirmation_page.dart';
import 'package:comwell_key_app/pregistration/pages/prereg_profile_page.dart';
@@ -7,6 +8,7 @@ import 'package:flutter/material.dart';
enum PreregistrationPage {
profile,
address,
+ payment,
upSales,
confirmation;
@@ -21,10 +23,8 @@ enum PreregistrationPage {
return PreregProfilePage(key: key);
case PreregistrationPage.address:
return PreregAddressPage(key: key);
- // case PreregistrationPage.payment:
- // return BlocProvider(
- // create: (context) => PaymentCardsCubit(),
- // child: const PaymentCardsPage());
+ case PreregistrationPage.payment:
+ return const PaymentCardsPage();
case PreregistrationPage.upSales:
return const PreregUpSalesCatalogPage();
case PreregistrationPage.confirmation:
diff --git a/comwell_key_app/lib/profile/components/comwell_club_container.dart b/comwell_key_app/lib/profile/components/comwell_club_container.dart
new file mode 100644
index 00000000..d2bd505f
--- /dev/null
+++ b/comwell_key_app/lib/profile/components/comwell_club_container.dart
@@ -0,0 +1,70 @@
+import 'package:comwell_key_app/profile/components/comwell_club_signup_bottom_sheet.dart';
+import 'package:comwell_key_app/profile_settings/model/user.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class ComwellClubContainer extends StatelessWidget {
+ final User user;
+ final VoidCallback onSignupClick;
+ const ComwellClubContainer({super.key, required this.user, required this.onSignupClick});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsets.symmetric(horizontal: 16),
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ decoration: BoxDecoration(
+ color: sandColor[10],
+ borderRadius: const BorderRadius.all(Radius.circular(10)),
+ ),
+ height: 100,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ GestureDetector(
+ onTap: () async {
+ await showModalBottomSheet<void>(
+ context: context,
+ isScrollControlled: true,
+ backgroundColor: Colors.white,
+ builder: (context) => ComwellClubSignupBottomSheet(user: user),
+ );
+ if (context.mounted) {
+ onSignupClick;
+ }
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("become_cc_member_title".tr(),
+ textAlign: TextAlign.start,
+ style: const TextStyle(
+ color: colorTertiary,
+ fontSize: 16,
+ fontWeight: FontWeight.w600)),
+ SizedBox(
+ width: 250,
+ child: Text(
+ "become_cc_member_subtitle".tr(),
+ textAlign: TextAlign.start,
+ maxLines: 4,
+ style: const TextStyle(
+ color: colorTertiary,
+ fontSize: 14,
+ fontWeight: FontWeight.w500),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const Icon(
+ Icons.chevron_right,
+ color: colorTertiary,
+ size: 24,
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/profile/components/profile_page_widget.dart b/comwell_key_app/lib/profile/components/profile_page_widget.dart
index cbfc1d7e..c53eba46 100644
--- a/comwell_key_app/lib/profile/components/profile_page_widget.dart
+++ b/comwell_key_app/lib/profile/components/profile_page_widget.dart
@@ -2,7 +2,7 @@ import 'package:comwell_key_app/common/components/comwell_card_component.dart';
import 'package:comwell_key_app/common/components/round_icon_button.dart';
import 'package:comwell_key_app/common/const.dart';
import 'package:comwell_key_app/profile/components/card_content_widget.dart';
-import 'package:comwell_key_app/profile/components/comwell_club_signup_bottom_sheet.dart';
+import 'package:comwell_key_app/profile/components/comwell_club_container.dart';
import 'package:comwell_key_app/profile/components/logout_dialog_widget.dart';
import 'package:comwell_key_app/profile/components/profile_settings_item.dart';
import 'package:comwell_key_app/profile/cubit/profile_cubit.dart';
@@ -22,8 +22,7 @@ class ProfilePageWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final user = cubit.state.user;
- final isActive = cubit.state.user!.clubLevel != null &&
- cubit.state.user!.clubLevel != '';
+ final isActive = cubit.state.user!.isClubMember;
return Column(
children: [
@@ -57,61 +56,9 @@ class ProfilePageWidget extends StatelessWidget {
children: [
const SizedBox(height: 10),
!isActive
- ? Container(
- margin: const EdgeInsets.symmetric(horizontal: 16),
- padding: const EdgeInsets.symmetric(vertical: 16),
- decoration: BoxDecoration(
- color: sandColor[10],
- borderRadius:
- const BorderRadius.all(Radius.circular(10)),
- ),
- height: 115,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- GestureDetector(
- onTap: () async {
- await showModalBottomSheet<void>(
- context: context,
- isScrollControlled: true,
- backgroundColor: Colors.white,
- builder: (context) =>
- ComwellClubSignupBottomSheet(user: user!),
- );
- cubit.init();
- },
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("become_cc_member_title".tr(),
- textAlign: TextAlign.start,
- style: const TextStyle(
- color: colorTertiary,
- fontSize: 16,
- fontWeight: FontWeight.w600)),
- SizedBox(
- width: 250,
- child: Text(
- "become_cc_member_subtitle".tr(),
- textAlign: TextAlign.start,
- maxLines: 4,
- style: const TextStyle(
- color: colorTertiary,
- fontSize: 14,
- fontWeight: FontWeight.w500),
- ),
- ),
- ],
- ),
- ),
- const Icon(
- Icons.chevron_right,
- color: colorTertiary,
- size: 24,
- ),
- ],
- ),
- )
+ ? ComwellClubContainer(user: user!, onSignupClick: () {
+ cubit.init();
+ })
: const SizedBox(),
const SizedBox(height: 10),
profileSettingsItem(
diff --git a/comwell_key_app/lib/profile/cubit/profile_cubit.dart b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
index eb3b91f8..87a70e81 100644
--- a/comwell_key_app/lib/profile/cubit/profile_cubit.dart
+++ b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
@@ -54,6 +54,7 @@ class ProfileCubit extends Cubit<ProfileState> {
void init() async {
emit(const ProfileState(isLoading: true, user: null, error: null));
+
try {
final user = await profileRepository.fetchProfileSettings();
sendPageViewEvent();
@@ -66,7 +67,7 @@ class ProfileCubit extends Cubit<ProfileState> {
void fetchRemoteProfile() async {
emit(const ProfileState(isLoading: true, user: null, error: null));
try {
- final user = await profileRepository.fetchRemoteProfile();
+ final user = await profileRepository.fetchProfileSettings(fetchRemote: true);
if (!isClosed) emit(ProfileState(user: user, isLoading: false));
} catch (e) {
diff --git a/comwell_key_app/lib/profile/profile_repository.dart b/comwell_key_app/lib/profile/profile_repository.dart
index 51ae97df..687e8e5a 100644
--- a/comwell_key_app/lib/profile/profile_repository.dart
+++ b/comwell_key_app/lib/profile/profile_repository.dart
@@ -1,12 +1,9 @@
import 'package:comwell_key_app/authentication/authentication_repository.dart';
import 'package:comwell_key_app/database/comwell_db.dart';
-import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/profile_settings/model/user.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
import 'package:comwell_key_app/services/api.dart';
-import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
import 'package:comwell_key_app/services/mappers/user_mapper.dart';
-import 'package:comwell_key_app/services/models/bookings_dto.dart';
import 'package:comwell_key_app/services/models/user_dto.dart';
import 'package:comwell_key_app/utils/json.dart';
import 'package:comwell_key_app/utils/locator.dart';
@@ -31,8 +28,19 @@ class ProfileRepository {
}
Future<User> signupForComwellClub(User user) async {
- final response = await api.signupForComwellClub(user);
- final data = response.data as Json;
+ try {
+ await api.signupForComwellClub();
+ return await _updateAndPersistUser(user);
+ } catch (e, st) {
+ debugPrint('Error during Comwell Club signup: $e');
+ debugPrintStack(stackTrace: st);
+ rethrow;
+ }
+ }
+
+ Future<User> _updateAndPersistUser(User user) async {
+ final userResponse = await api.updateUser(user.toSimpleUserDto());
+ final data = userResponse.data as Json;
final userDto = UserDto.fromJson(data);
final updatedUser = userDto.toUser();
await locator<ComwellDatabase>().userDAO.saveUser(userDto);
@@ -47,50 +55,7 @@ class ProfileRepository {
return user;
}
- Future<Booking> getRemoteBookingDetails(String hmsConfirmationNumber, String hotelCode) async {
- final response = await api.getBookingDetails(hmsConfirmationNumber, hotelCode);
- return response!.toBooking(user.id, []);
- }
-
-
- Future<Booking> getBookingDetails(String hmsConfirmationNumber, String hotelCode) async {
- user = await fetchProfileSettings();
-
- final booking = await _checkIfBookingDetailsExists(hmsConfirmationNumber);
- if (booking != null) {
- return booking;
- }
-
- final newBooking = await _fetchAndSaveBookingDetailsToDatabase(hmsConfirmationNumber, hotelCode);
- return newBooking;
- }
-
- Future<Booking?> _checkIfBookingDetailsExists(String hmsConfirmationNumber) async {
- try {
- final booking = await locator<ComwellDatabase>()
- .bookingsDao
- .getBookingDetails(hmsConfirmationNumber, user.id, []);
- return booking;
- } on Exception catch (e) {
- debugPrint("Error checking if booking details exists: $e");
- return null;
- }
- }
-
- Future<Booking> _fetchAndSaveBookingDetailsToDatabase(
- String hmsConfirmationNumber, String hotelCode) async {
- try {
- final response = await api.getBookingDetails(hmsConfirmationNumber, hotelCode);
- await locator<ComwellDatabase>().bookingsDao.insertBookings(
- BookingsDTO(current: [response!], past: [], cancelled: []));
- final booking = response.toBooking(user.id, []);
- debugPrint("Booking saved to database: ${booking.confirmationId}");
- return booking;
- } on Exception catch (e) {
- debugPrint("Error fetching booking details: $e");
- rethrow;
- }
- }
+
Future<User?> _checkIfProfileSettingsExists() async {
try {
@@ -116,7 +81,11 @@ class ProfileRepository {
}
}
- Future<User> fetchProfileSettings() async {
+ Future<User> fetchProfileSettings({bool fetchRemote = false}) async {
+ if (fetchRemote) {
+ final user = await fetchRemoteProfile();
+ return user;
+ }
final user = await _checkIfProfileSettingsExists();
if (user != null) {
return user;
diff --git a/comwell_key_app/lib/profile_settings/components/intl_phone_field.dart b/comwell_key_app/lib/profile_settings/components/intl_phone_field.dart
index 233409f2..b27ada09 100644
--- a/comwell_key_app/lib/profile_settings/components/intl_phone_field.dart
+++ b/comwell_key_app/lib/profile_settings/components/intl_phone_field.dart
@@ -94,8 +94,9 @@ class IntlPhoneFieldState extends State<IntlPhoneField> {
style: theme.textTheme.headlineSmall,
onSubmitted: widget.onSubmitted,
onChanged: widget.onPhoneNumberChanged,
-
- keyboardType: TextInputType.number,
+ keyboardType: const TextInputType.numberWithOptions(signed: true),
+ textInputAction: TextInputAction.done,
+ onEditingComplete: () => _focusNode.unfocus(),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
diff --git a/comwell_key_app/lib/profile_settings/cubit/profile_settings_cubit.dart b/comwell_key_app/lib/profile_settings/cubit/profile_settings_cubit.dart
index d67fc6b6..764d15f6 100644
--- a/comwell_key_app/lib/profile_settings/cubit/profile_settings_cubit.dart
+++ b/comwell_key_app/lib/profile_settings/cubit/profile_settings_cubit.dart
@@ -101,9 +101,7 @@ class ProfileSettingsCubit extends Cubit<ProfileSettingsState> {
points: state.user!.points,
addressCountry: state.user!.addressCountry,
gender: state.user!.gender,
- clubId: state.user!.clubId,
- clubLevel: state.user!.clubLevel,
- clubLevelName: state.user!.clubLevelName,
+ isClubMember: state.user!.isClubMember,
);
await profileSettingsRepository.updateUser(updatedUser);
diff --git a/comwell_key_app/lib/profile_settings/model/user.dart b/comwell_key_app/lib/profile_settings/model/user.dart
index 5eed41f0..c332f027 100644
--- a/comwell_key_app/lib/profile_settings/model/user.dart
+++ b/comwell_key_app/lib/profile_settings/model/user.dart
@@ -2,72 +2,117 @@ import 'package:comwell_key_app/profile_settings/model/address.dart';
class User {
final int id;
+ final String userId;
+ final String? hmsId;
final String firstName;
final String lastName;
final String addressCountry;
final String phoneNumber;
final String email;
+ final bool emailVerified;
final Address address;
final DateTime birthDate;
final String shopperReference;
final int points;
final String? gender;
- final String? clubId;
- final String? clubLevel;
- final String? clubLevelName;
+ final bool isClubMember;
+ final DateTime? createDate;
+ final String? companyId;
+ final String? companyName;
+ final String? symplifyId;
+ final DateTime? signUpDate;
+ final String? signUpCampaign;
+ final String? signUpSource;
+ final String locale;
+ final bool wasRecentlyCreated;
+ final String? nationality;
+
User({
required this.id,
+ required this.userId,
+ this.hmsId,
required this.firstName,
required this.lastName,
required this.addressCountry,
required this.phoneNumber,
required this.email,
+ required this.emailVerified,
required this.address,
required this.birthDate,
required this.shopperReference,
required this.points,
this.gender,
- this.clubId,
- this.clubLevel,
- this.clubLevelName,
+ required this.isClubMember,
+ this.createDate,
+ this.companyId = '',
+ this.companyName = '',
+ this.symplifyId = '',
+ this.signUpDate,
+ this.signUpCampaign,
+ this.signUpSource,
+ required this.locale,
+ required this.wasRecentlyCreated,
+ this.nationality,
});
@override
String toString() {
- return 'User(id: $id, firstName: $firstName, lastName: $lastName, addressCountry: $addressCountry, phone: $phoneNumber, email: $email, address: $address, birthDate: $birthDate, shopperReference: $shopperReference, points: $points, clubLevel: $clubLevel, clubLevelName: $clubLevelName)';
+ return 'User(id: $id, firstName: $firstName, lastName: $lastName, addressCountry: $addressCountry, phone: $phoneNumber, email: $email, address: $address, birthDate: $birthDate, shopperReference: $shopperReference, points: $points, isClubMember: $isClubMember)';
}
User copyWith({
int? id,
+ String? userId,
+ String? hmsId,
String? firstName,
String? lastName,
String? addressCountry,
String? phoneNumber,
String? email,
+ bool? emailVerified,
Address? address,
String? shopperReference,
DateTime? birthDate,
int? points,
String? gender,
- String? clubId,
- String? clubLevel,
- String? clubLevelName,
+ bool? isClubMember,
+ DateTime? createDate,
+ String? companyId,
+ String? companyName,
+ String? symplifyId,
+ DateTime? signUpDate,
+ String? signUpCampaign,
+ String? signUpSource,
+ String? locale,
+ bool? wasRecentlyCreated,
+ String? nationality,
}) {
return User(
id: id ?? this.id,
+ userId: userId ?? this.userId,
+ hmsId: hmsId ?? this.hmsId,
firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName,
addressCountry: addressCountry ?? this.addressCountry,
phoneNumber: phoneNumber ?? this.phoneNumber,
email: email ?? this.email,
+ emailVerified: emailVerified ?? this.emailVerified,
address: address ?? this.address,
birthDate: birthDate ?? this.birthDate,
shopperReference: shopperReference ?? this.shopperReference,
points: points ?? this.points,
gender: gender ?? this.gender,
- clubId: clubId ?? this.clubId,
- clubLevel: clubLevel ?? this.clubLevel,
- clubLevelName: clubLevelName ?? this.clubLevelName,
+ isClubMember: isClubMember ?? this.isClubMember,
+ createDate: createDate ?? this.createDate,
+ companyId: companyId ?? this.companyId,
+ companyName: companyName ?? this.companyName,
+ symplifyId: symplifyId ?? this.symplifyId,
+ signUpDate: signUpDate ?? this.signUpDate,
+ signUpCampaign: signUpCampaign ?? this.signUpCampaign,
+ signUpSource: signUpSource ?? this.signUpSource,
+ locale: locale ?? this.locale,
+ wasRecentlyCreated: wasRecentlyCreated ?? this.wasRecentlyCreated,
+ nationality: nationality ?? this.nationality,
);
}
}
diff --git a/comwell_key_app/lib/profile_settings/repostiory/profile_settings_repository.dart b/comwell_key_app/lib/profile_settings/repostiory/profile_settings_repository.dart
index 18f9fe47..83baa6c1 100644
--- a/comwell_key_app/lib/profile_settings/repostiory/profile_settings_repository.dart
+++ b/comwell_key_app/lib/profile_settings/repostiory/profile_settings_repository.dart
@@ -12,7 +12,7 @@ class ProfileSettingsRepository {
final Api api = Api();
Future<User> updateUser(User updatedUser) async {
- final userDto = updatedUser.toUserDto();
+ final userDto = updatedUser.toSimpleUserDto();
final response = await api.updateUser(userDto);
final data = response.data as Json;
diff --git a/comwell_key_app/lib/push_notifications/cubit/push_notification_cubit.dart b/comwell_key_app/lib/push_notifications/cubit/push_notification_cubit.dart
new file mode 100644
index 00000000..a0cb52ee
--- /dev/null
+++ b/comwell_key_app/lib/push_notifications/cubit/push_notification_cubit.dart
@@ -0,0 +1,88 @@
+import 'dart:io';
+
+import 'package:comwell_key_app/push_notifications/cubit/push_notification_state.dart';
+import 'package:comwell_key_app/push_notifications/push_notification_repository.dart';
+import 'package:comwell_key_app/utils/locator.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+
+class PushNotificationCubit extends Cubit<PushNotificationState> {
+ final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
+ final PushNotificationRepository pushNotificationRepository =
+ locator<PushNotificationRepository>();
+ PushNotificationCubit() : super(PushNotificationState());
+
+ Future<void> initialize() async {
+ try {
+ await _firebaseMessaging.requestPermission();
+
+ // Ensure notifications show while app is in foreground on iOS
+ await _firebaseMessaging.setForegroundNotificationPresentationOptions(
+ alert: true,
+ badge: true,
+ sound: true,
+ );
+
+ await getFcmToken();
+ await registerPushTokenToSymplify();
+
+ // Keep tokens fresh
+ FirebaseMessaging.instance.onTokenRefresh.listen((String newToken) async {
+ String? refreshedApnsToken = state.apnsToken;
+ if (Platform.isIOS) {
+ // Re-read APNs token alongside refreshed FCM token
+ refreshedApnsToken = await _firebaseMessaging.getAPNSToken();
+
+ debugPrint('APNs Token (refreshed): $refreshedApnsToken');
+
+ }
+ debugPrint('FCM Token (refreshed): $newToken');
+ emit(state.copyWith(fcmToken: newToken, apnsToken: refreshedApnsToken));
+ });
+
+ FirebaseMessaging.onMessage.listen((RemoteMessage message) {
+ debugPrint('Message data: ${message.data}');
+ if (message.notification != null) {
+ emit(state.copyWith(message: message.notification?.title));
+ }
+ });
+ FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
+ emit(state.copyWith(message: message.notification?.title));
+ });
+ } catch (e) {
+ debugPrint('Error: $e');
+ emit(state.copyWith(error: e.toString()));
+ }
+ }
+
+ Future<void> registerPushTokenToSymplify() async {
+ await pushNotificationRepository.registerDeviceAndProfile(state.fcmToken ?? '');
+
+ }
+
+ Future<void> getFcmToken() async {
+ String? apnsToken;
+ if (Platform.isIOS) {
+ // Give iOS time to register with APNs before requesting tokens
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+ apnsToken = await _firebaseMessaging.getAPNSToken();
+ debugPrint('APNs Token: $apnsToken');
+ }
+
+ final String? fcmToken = await _firebaseMessaging.getToken();
+ debugPrint('FCM Token: $fcmToken');
+ emit(state.copyWith(fcmToken: fcmToken, apnsToken: apnsToken));
+ }
+
+ Future<void> getApnsToken() async {
+ String? apnsToken;
+ if (Platform.isIOS) {
+ // Give iOS time to register with APNs before requesting tokens
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+ apnsToken = await _firebaseMessaging.getAPNSToken();
+ debugPrint('APNs Token: $apnsToken');
+ }
+ emit(state.copyWith(apnsToken: apnsToken));
+ }
+}
diff --git a/comwell_key_app/lib/push_notifications/cubit/push_notification_state.dart b/comwell_key_app/lib/push_notifications/cubit/push_notification_state.dart
new file mode 100644
index 00000000..793d185c
--- /dev/null
+++ b/comwell_key_app/lib/push_notifications/cubit/push_notification_state.dart
@@ -0,0 +1,19 @@
+class PushNotificationState {
+ final String? message;
+ final String? error;
+ final String? fcmToken;
+ final String? apnsToken;
+
+ PushNotificationState({this.message, this.error, this.fcmToken, this.apnsToken});
+
+ PushNotificationState copyWith({String? message, String? error, String? fcmToken, String? apnsToken}) {
+ return PushNotificationState(
+ message: message ?? this.message,
+ error: error ?? this.error,
+ fcmToken: fcmToken ?? this.fcmToken,
+ apnsToken: apnsToken ?? this.apnsToken,
+ );
+ }
+
+ List<Object?> get props => [message, error, fcmToken, apnsToken];
+}
diff --git a/comwell_key_app/lib/push_notifications/push_notification_repository.dart b/comwell_key_app/lib/push_notifications/push_notification_repository.dart
new file mode 100644
index 00000000..9b259168
--- /dev/null
+++ b/comwell_key_app/lib/push_notifications/push_notification_repository.dart
@@ -0,0 +1,13 @@
+import 'package:comwell_key_app/services/api.dart';
+
+class PushNotificationRepository {
+ final Api api = Api();
+
+ Future<void> registerDeviceAndProfile(String token) async {
+ try {
+ await api.registerDeviceAndProfileSymplify(token);
+ } catch (e) {
+ throw Exception('Failed to register push token: $e');
+ }
+ }
+}
diff --git a/comwell_key_app/lib/push_notifications/push_notification_widget.dart b/comwell_key_app/lib/push_notifications/push_notification_widget.dart
new file mode 100644
index 00000000..867639e8
--- /dev/null
+++ b/comwell_key_app/lib/push_notifications/push_notification_widget.dart
@@ -0,0 +1,17 @@
+import 'package:comwell_key_app/push_notifications/cubit/push_notification_cubit.dart';
+import 'package:comwell_key_app/push_notifications/cubit/push_notification_state.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class PushNotificationWidget extends StatelessWidget {
+ const PushNotificationWidget({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder<PushNotificationCubit, PushNotificationState>(
+ builder: (context, state) {
+ return const SizedBox.shrink();
+ },
+ );
+ }
+}
diff --git a/comwell_key_app/lib/received_shared_booking/cubit/received_shared_booking_cubit.dart b/comwell_key_app/lib/received_shared_booking/cubit/received_shared_booking_cubit.dart
new file mode 100644
index 00000000..85e81523
--- /dev/null
+++ b/comwell_key_app/lib/received_shared_booking/cubit/received_shared_booking_cubit.dart
@@ -0,0 +1,41 @@
+import 'package:bloc/bloc.dart';
+import 'package:comwell_key_app/booking_details/booking_details_repository.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/profile/profile_repository.dart';
+import 'package:comwell_key_app/received_shared_booking/cubit/received_shared_booking_state.dart';
+import 'package:comwell_key_app/share/share_booking_repository.dart';
+import 'package:comwell_key_app/utils/locator.dart';
+import 'package:flutter/material.dart';
+
+class ReceivedSharedBookingCubit extends Cubit<ReceivedSharedBookingState> {
+ final String sharingId;
+ final String hotelCode;
+ late String hmsConfirmationNumber;
+ late Booking booking;
+
+ final ShareBookingRepository shareBookingRepository =
+ locator<ShareBookingRepository>();
+ final ProfileRepository profileRepository = locator<ProfileRepository>();
+ final BookingDetailsRepository bookingDetailsRepository = locator<BookingDetailsRepository>();
+
+ ReceivedSharedBookingCubit(this.sharingId, this.hotelCode)
+ : super(const ReceivedSharedBookingState.loading());
+
+ void init() async {
+ emit(const ReceivedSharedBookingState.loading());
+
+ try {
+ final response = await shareBookingRepository.consumeRoomSharingLink(
+ sharingId, hotelCode);
+ hmsConfirmationNumber = response.confirmationNumber;
+
+ booking = await bookingDetailsRepository.getBookingDetails(
+ hmsConfirmationNumber, hotelCode);
+
+ emit(const ReceivedSharedBookingState.loaded());
+ } catch (e) {
+ debugPrint("Error consuming room sharing link: $e");
+ emit(ReceivedSharedBookingState.error(e is Error ? e : Error()));
+ }
+ }
+}
diff --git a/comwell_key_app/lib/received_shared_booking/cubit/received_shared_booking_state.dart b/comwell_key_app/lib/received_shared_booking/cubit/received_shared_booking_state.dart
new file mode 100644
index 00000000..835a7eb0
--- /dev/null
+++ b/comwell_key_app/lib/received_shared_booking/cubit/received_shared_booking_state.dart
@@ -0,0 +1,26 @@
+import 'package:equatable/equatable.dart';
+
+class ReceivedSharedBookingState extends Equatable {
+ final bool isLoading;
+ final Error? error;
+
+ const ReceivedSharedBookingState({required this.isLoading, required this.error});
+
+ const ReceivedSharedBookingState.initial() : this(isLoading: false, error: null);
+
+ const ReceivedSharedBookingState.loading() : this(isLoading: true, error: null);
+
+ const ReceivedSharedBookingState.error(Error error) : this(isLoading: false, error: error);
+
+ const ReceivedSharedBookingState.loaded() : this(isLoading: false, error: null);
+
+ @override
+ List<Object?> get props => [isLoading, error];
+
+ ReceivedSharedBookingState copyWith({
+ bool? isLoading,
+ Error? error,
+ }) {
+ return ReceivedSharedBookingState(isLoading: isLoading ?? this.isLoading, error: error ?? this.error);
+ }
+}
diff --git a/comwell_key_app/lib/received_shared_booking/received_shared_booking_page.dart b/comwell_key_app/lib/received_shared_booking/received_shared_booking_page.dart
new file mode 100644
index 00000000..042b7c43
--- /dev/null
+++ b/comwell_key_app/lib/received_shared_booking/received_shared_booking_page.dart
@@ -0,0 +1,60 @@
+import 'package:comwell_key_app/common/components/comwell_error_widget.dart';
+import 'package:comwell_key_app/common/components/shimmer_loader/share_room_shimmer_loader.dart';
+import 'package:comwell_key_app/received_shared_booking/cubit/received_shared_booking_cubit.dart';
+import 'package:comwell_key_app/received_shared_booking/cubit/received_shared_booking_state.dart';
+import 'package:comwell_key_app/routing/app_routes.dart';
+import 'package:comwell_key_app/utils/templates/share_booking_base_template.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
+
+class ReceivedSharedBookingPage extends StatelessWidget {
+ const ReceivedSharedBookingPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder<ReceivedSharedBookingCubit, ReceivedSharedBookingState>(
+ builder: (context, state) {
+ final cubit = context.read<ReceivedSharedBookingCubit>();
+ if (state.isLoading) {
+ return const ShareRoomShimmerLoader();
+ }
+ if (state.error != null) {
+ return Scaffold(
+ body: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ ComwellErrorWidget(
+ title: "share_booking_error_title".tr(),
+ subtitle: "share_booking_error_subtitle".tr(),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32.0),
+ child: ElevatedButton(
+ onPressed: () {
+ context.goNamed(AppRoutes.overview.name);
+ },
+ child: Text("home_page".tr(),
+ style: Theme.of(context)
+ .textTheme
+ .bodyMedium
+ ?.copyWith(color: Colors.white))),
+ )
+ ],
+ ),
+ );
+ }
+ return ShareBookingBaseTemplate(
+ booking: cubit.booking,
+ onClicked: () {
+ context.goNamed(AppRoutes.overview.name);
+ },
+ isShared: true,
+ isLoading: state.isLoading,
+ error: state.error);
+ },
+ );
+ }
+}
diff --git a/comwell_key_app/lib/received_shared_room/cubit/received_shared_room_cubit.dart b/comwell_key_app/lib/received_shared_room/cubit/received_shared_room_cubit.dart
new file mode 100644
index 00000000..081bacb3
--- /dev/null
+++ b/comwell_key_app/lib/received_shared_room/cubit/received_shared_room_cubit.dart
@@ -0,0 +1,37 @@
+import 'package:comwell_key_app/booking_details/booking_details_repository.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/profile/profile_repository.dart';
+import 'package:comwell_key_app/received_shared_room/cubit/received_shared_room_state.dart';
+import 'package:comwell_key_app/share/share_booking_repository.dart';
+import 'package:comwell_key_app/utils/locator.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class ReceivedSharedRoomCubit extends Cubit<ReceivedSharedRoomState> {
+ final String sharingId;
+ final String hotelCode;
+ late String hmsConfirmationNumber;
+ late Booking booking;
+
+ final ShareBookingRepository shareBookingRepository =
+ locator<ShareBookingRepository>();
+ final ProfileRepository profileRepository = locator<ProfileRepository>();
+ final BookingDetailsRepository bookingDetailsRepository = locator<BookingDetailsRepository>();
+
+ ReceivedSharedRoomCubit(this.sharingId, this.hotelCode)
+ : super(const ReceivedSharedRoomState.loading());
+
+ void init() async {
+ emit(const ReceivedSharedRoomState.loading());
+ try {
+ final response = await shareBookingRepository.consumeRoomSharingLink(sharingId, hotelCode);
+ hmsConfirmationNumber = response.confirmationNumber;
+
+ booking = await bookingDetailsRepository.getBookingDetails(
+ hmsConfirmationNumber, hotelCode);
+
+ emit(const ReceivedSharedRoomState.loaded());
+ } catch (e) {
+ emit(ReceivedSharedRoomState.error(e is Error ? e : Error()));
+ }
+ }
+}
diff --git a/comwell_key_app/lib/received_shared_room/cubit/received_shared_room_state.dart b/comwell_key_app/lib/received_shared_room/cubit/received_shared_room_state.dart
new file mode 100644
index 00000000..6a3d463a
--- /dev/null
+++ b/comwell_key_app/lib/received_shared_room/cubit/received_shared_room_state.dart
@@ -0,0 +1,28 @@
+
+import 'package:equatable/equatable.dart';
+
+class ReceivedSharedRoomState extends Equatable {
+ final Error? error;
+ final bool isLoading;
+
+
+ const ReceivedSharedRoomState({this.error, required this.isLoading});
+
+ const ReceivedSharedRoomState.initial() : this(error: null, isLoading: false);
+
+ const ReceivedSharedRoomState.loading() : this(error: null, isLoading: true);
+
+ const ReceivedSharedRoomState.error(Error error) : this(error: error, isLoading: false);
+
+ const ReceivedSharedRoomState.loaded() : this(error: null, isLoading: false);
+
+ @override
+ List<Object?> get props => [error, isLoading];
+
+ ReceivedSharedRoomState copyWith({
+ Error? error,
+ bool? isLoading,
+ }) {
+ return ReceivedSharedRoomState(error: error ?? this.error, isLoading: isLoading ?? this.isLoading);
+ }
+}
\ No newline at end of file
diff --git a/comwell_key_app/lib/received_shared_room/received_shared_room_page.dart b/comwell_key_app/lib/received_shared_room/received_shared_room_page.dart
new file mode 100644
index 00000000..ff56ed4f
--- /dev/null
+++ b/comwell_key_app/lib/received_shared_room/received_shared_room_page.dart
@@ -0,0 +1,33 @@
+import 'package:comwell_key_app/choose_share_room/pages/share_room_base_page_template.dart';
+import 'package:comwell_key_app/common/components/shimmer_loader/share_room_shimmer_loader.dart';
+import 'package:comwell_key_app/received_shared_room/cubit/received_shared_room_cubit.dart';
+import 'package:comwell_key_app/received_shared_room/cubit/received_shared_room_state.dart';
+import 'package:comwell_key_app/routing/app_routes.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
+
+class ReceivedSharedRoomPage extends StatelessWidget {
+ const ReceivedSharedRoomPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder<ReceivedSharedRoomCubit, ReceivedSharedRoomState>(
+ builder: (context, state) {
+ final cubit = context.read<ReceivedSharedRoomCubit>();
+ if (state.isLoading) {
+ return const ShareRoomShimmerLoader();
+ }
+ if (state.error != null) {
+ return Center(child: Text(state.error.toString()));
+ }
+
+ return ShareRoomBasePageTemplate(
+ booking: cubit.booking,
+ onClicked: () {
+ context.goNamed(AppRoutes.overview.name);
+ },
+ isShared: true);
+ });
+ }
+}
diff --git a/comwell_key_app/lib/routing/app_router.dart b/comwell_key_app/lib/routing/app_router.dart
index 3c3dceb2..db62cceb 100644
--- a/comwell_key_app/lib/routing/app_router.dart
+++ b/comwell_key_app/lib/routing/app_router.dart
@@ -17,6 +17,7 @@ import 'package:comwell_key_app/common/const.dart';
import 'package:comwell_key_app/contact/contact_page.dart';
import 'package:comwell_key_app/contact/cubit/contact_cubit.dart';
import 'package:comwell_key_app/contact/repository/contact_repository.dart';
+import 'package:comwell_key_app/find_booking/cubit/find_booking_cubit.dart';
import 'package:comwell_key_app/find_booking/find_booking_page.dart';
import 'package:comwell_key_app/find_booking/loading_page.dart';
import 'package:comwell_key_app/force_update/force_update_page.dart';
@@ -54,6 +55,10 @@ import 'package:comwell_key_app/profile_settings/components/change_password_moda
import 'package:comwell_key_app/profile_settings/cubit/profile_settings_cubit.dart';
import 'package:comwell_key_app/profile_settings/profile_settings_page.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
+import 'package:comwell_key_app/received_shared_booking/cubit/received_shared_booking_cubit.dart';
+import 'package:comwell_key_app/received_shared_booking/received_shared_booking_page.dart';
+import 'package:comwell_key_app/received_shared_room/cubit/received_shared_room_cubit.dart';
+import 'package:comwell_key_app/received_shared_room/received_shared_room_page.dart';
import 'package:comwell_key_app/redeem_debug/redeem_page.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/routing/go_router_observer.dart';
@@ -65,7 +70,6 @@ import 'package:comwell_key_app/up_sales/cubit/up_sales_state.dart';
import 'package:comwell_key_app/up_sales/models/addon_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/room_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/room_upgrade_list.dart';
-import 'package:comwell_key_app/up_sales/models/up_sales.dart';
import 'package:comwell_key_app/up_sales/pages/addon_upgrade_page.dart';
import 'package:comwell_key_app/up_sales/pages/processing/up_sales_error_page.dart';
import 'package:comwell_key_app/up_sales/pages/room_upgrade_page.dart';
@@ -95,7 +99,8 @@ final router = GoRouter(
navigatorKey: _rootNavigatorKey,
debugLogDiagnostics: true,
observers: [GoRouterObserver()],
- refreshListenable: StreamToListenable([locator<AuthenticationRepository>().broadcast]),
+ refreshListenable:
+ StreamToListenable([locator<AuthenticationRepository>().broadcast]),
redirect: (context, state) async {
final authRepo = locator<AuthenticationRepository>();
final status = authRepo.statusBuffer;
@@ -104,12 +109,21 @@ final router = GoRouter(
status == AuthenticationStatus.unauthenticated ||
status == AuthenticationStatus.forcedUnauthenticated;
- if (status == AuthenticationStatus.unknown) {
- bool doesTokenExist = await authRepo.doesTokenExist();
- if (doesTokenExist) {
- authRepo.logIn();
- }
+ if (status == AuthenticationStatus.unknown) {
+ bool doesTokenExist = await authRepo.doesTokenExist();
+ if (doesTokenExist) {
+ authRepo.logIn();
}
+ }
+
+ if (state.uri.host == 'share-room') {
+ final sharingType = state.uri.queryParameters['sharingType'];
+ if (sharingType == 'RoomDistribution') {
+ return "/${AppRoutes.receivedSharedRoom.name}?sharingId=${state.uri.queryParameters['sharingId']}&hotelCode=${state.uri.queryParameters['hotelCode']}";
+ } else {
+ return "/${AppRoutes.receivedSharedBooking.name}?sharingId=${state.uri.queryParameters['sharingId']}&hotelCode=${state.uri.queryParameters['hotelCode']}";
+ }
+ }
// Redirect to the login page if the user is not authenticated, and if authenticated, do not show the login page
if (isUnAuthenticated && !state.matchedLocation.contains("/login")) {
@@ -124,326 +138,202 @@ final router = GoRouter(
return null;
},
routes: <RouteBase>[
- ShellRoute(
- navigatorKey: _shellNavigatorKey,
- parentNavigatorKey: _rootNavigatorKey,
- pageBuilder: (context, state, child) {
- return NoTransitionPage(
- child: BlocProvider(
- create: (_) => HotelInformationCubit(
- hotelInformationRepository:
- locator<HotelInformationRepository>(),
- booking: state.extra as Booking,
- )..init(),
- child: HotelInformationPage(child: child),
- ),
- );
- },
- routes: [
- GoRoute(
- path: "/${AppRoutes.hotelInformation.name}",
- name: AppRoutes.hotelInformation.name,
- builder: (context, state) {
- return const HotelInformationMenu();
- }),
- GoRoute(
- path:
- "/${AppRoutes.hotelInformation.name}/${AppRoutes.facility.name}",
- name:
- "${AppRoutes.hotelInformation.name}/${AppRoutes.facility.name}",
- builder: (context, state) {
- return FacilityPage(facility: state.extra as Facility);
- }),
- ]),
- GoRoute(
- path: '/${AppRoutes.contact.name}',
- name: AppRoutes.contact.name,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return BlocProvider<ContactCubit>(
- create: (BuildContext context) => ContactCubit(
- contactRepository: locator<ContactRepository>(),
- overviewRepository: locator<OverviewRepository>(),
- profileRepository: locator<ProfileRepository>())
- ..init(),
- child: ContactPage(hotelCode: booking.hotelCode),
- );
- }),
- GoRoute(
- path: '/loading_page',
- name: AppRoutes.loadingPage.name,
- builder: (context, state) {
- return const LoadingPage();
- }),
- GoRoute(
- path: "/find_booking",
- name: AppRoutes.findBooking.name,
- builder: (context, state) => const FindBookingPage(),
- ),
- GoRoute(
- path: "/${AppRoutes.shareBooking.name}",
- name: AppRoutes.shareBooking.name,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return BlocProvider(
- create: (context) => ShareBookingCubit(),
- child: ShareBookingPage(booking: booking),
- );
- },
- ),
- GoRoute(
- path: "/${AppRoutes.bookingDetails.name}/:id",
- name: "booking_deep_link",
- builder: (context, state) {
- final bookingId = state.pathParameters['id']!;
- return BlocProvider<OverviewCubit>(
- create: (context) => OverviewCubit(locator<OverviewRepository>())
- ..findBookingById(bookingId),
- child: BlocListener<OverviewCubit, OverviewState>(
- listener: (context, state) {},
- child: const LoadingPage(),
+ ShellRoute(
+ navigatorKey: _shellNavigatorKey,
+ parentNavigatorKey: _rootNavigatorKey,
+ pageBuilder: (context, state, child) {
+ return NoTransitionPage(
+ child: BlocProvider(
+ create: (_) => HotelInformationCubit(
+ hotelInformationRepository:
+ locator<HotelInformationRepository>(),
+ booking: state.extra as Booking,
+ )..init(),
+ child: HotelInformationPage(child: child),
),
);
},
- ),
- GoRoute(
- path: "/${AppRoutes.checkIn.name}",
- name: AppRoutes.checkIn.name,
- builder: (context, state) {
- return BlocProvider(
- create: (context) {
- final extras = state.extra as List<dynamic>;
- final booking = extras[0] as Booking;
- final onlyKeys = extras[1] as bool;
- if (onlyKeys) {
- return CheckInCubit.initialOnlyKeys(booking);
- }
- return CheckInCubit(booking);
- },
- child: const CheckInPage(),
- );
- }),
- GoRoute(
- path: "/${AppRoutes.overview.name}",
- name: AppRoutes.overview.name,
- builder: (context, state) {
- return const OverviewPage();
- },
routes: [
GoRoute(
- path: "profile",
- name: AppRoutes.profile.name,
- builder: (context, state) {
- return BlocProvider<ProfileCubit>(
- create: (BuildContext context) => ProfileCubit(
- profileRepository: locator<ProfileRepository>(),
- authenticationRepository:
- locator<AuthenticationRepository>())
- ..init(),
- child: const ProfilePage(), //mobileKey: key);
- );
- },
- routes: [
- GoRoute(
- path: "profile_settings",
- name: AppRoutes.profileSettings.name,
- builder: (context, state) {
- return BlocProvider<ProfileSettingsCubit>(
- create: (BuildContext context) => ProfileSettingsCubit(
- profileRepository: locator<ProfileRepository>(),
- profileSettingsRepository:
- locator<ProfileSettingsRepository>(),
- authenticationRepository:
- locator<AuthenticationRepository>())
- ..init(),
- child: const ProfileSettingsPage(),
- );
- },
- routes: [
- GoRoute(
- path: 'change_password',
- name: AppRoutes.changePassword.name,
- builder: (context, state) {
- return const ChangePasswordModal();
- })
- ]),
- ],
- ),
- GoRoute(
- path: AppRoutes.pastCancelledBookings.name,
- name: AppRoutes.pastCancelledBookings.name,
+ path: "/${AppRoutes.hotelInformation.name}",
+ name: AppRoutes.hotelInformation.name,
builder: (context, state) {
- Booking booking = state.extra as Booking;
- return PastCancelledBookingDetailPage(booking: booking);
+ return const HotelInformationMenu();
}),
GoRoute(
- path: AppRoutes.bookingDetails.name,
- name: AppRoutes.bookingDetails.name,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return BlocProvider<BookingDetailsBloc>(
- create: (BuildContext context) => BookingDetailsBloc(booking,
- bookingDetailsRepository:
- locator<BookingDetailsRepository>(),
- profileRepository: locator<ProfileRepository>(),
- upSaleRepository: locator<UpSalesRepository>(),
- houseKeepingRepository:
- locator<HouseKeepingRepository>()),
- child: const BookingDetailsPage(),
- );
- },
- routes: [
- GoRoute(
- path: "key",
- name: AppRoutes.key.name,
- builder: (context, state) {
- return const KeyPage(); //mobileKey: key);
- },
- ),
- ],
+ path:
+ "/${AppRoutes.hotelInformation.name}/${AppRoutes.facility.name}",
+ name:
+ "${AppRoutes.hotelInformation.name}/${AppRoutes.facility.name}",
+ builder: (context, state) {
+ return FacilityPage(facility: state.extra as Facility);
+ }),
+ ]),
+ GoRoute(
+ path: '/${AppRoutes.contact.name}',
+ name: AppRoutes.contact.name,
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return BlocProvider<ContactCubit>(
+ create: (BuildContext context) => ContactCubit(
+ contactRepository: locator<ContactRepository>(),
+ overviewRepository: locator<OverviewRepository>(),
+ profileRepository: locator<ProfileRepository>())
+ ..init(),
+ child: ContactPage(hotelCode: booking.hotelCode),
+ );
+ }),
+
+ GoRoute(
+ path: "/${AppRoutes.findBooking.name}",
+ name: AppRoutes.findBooking.name,
+ builder: (context, state) {
+ return BlocProvider(
+ create: (context) => FindBookingCubit(),
+ child: const FindBookingPage(),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.shareBooking.name}",
+ name: AppRoutes.shareBooking.name,
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return BlocProvider(
+ create: (context) => ShareBookingCubit(),
+ child: ShareBookingPage(booking: booking),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.bookingDetails.name}/:id",
+ name: "booking_deep_link",
+ builder: (context, state) {
+ final bookingId = state.pathParameters['id']!;
+ return BlocProvider<OverviewCubit>(
+ create: (context) => OverviewCubit(locator<OverviewRepository>())
+ ..findBookingById(bookingId),
+ child: BlocListener<OverviewCubit, OverviewState>(
+ listener: (context, state) {},
+ child: const LoadingPage(),
),
- ],
- ),
- ShellRoute(
- pageBuilder: (context, state, child) {
- return NoTransitionPage(
- child: BlocProvider(
- create: (context) => MyBookingCubit(
- locator<MyBookingRepository>(),
- context.read<PaymentCubit>(),
- initialBooking: state.extra as Booking)
- ..init(),
- child: child));
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.loadingPage.name}",
+ name: AppRoutes.loadingPage.name,
+ builder: (context, state) {
+ return BlocProvider(
+ create: (context) => FindBookingCubit(),
+ child: const LoadingPage(),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.checkIn.name}",
+ name: AppRoutes.checkIn.name,
+ builder: (context, state) {
+ return BlocProvider(
+ create: (context) {
+ final extras = state.extra as List<dynamic>;
+ final booking = extras[0] as Booking;
+ final onlyKeys = extras[1] as bool;
+ if (onlyKeys) {
+ return CheckInCubit.initialOnlyKeys(booking);
+ }
+ return CheckInCubit(booking);
+ },
+ child: const CheckInPage(),
+ );
+ }),
+ GoRoute(
+ path: "/${AppRoutes.overview.name}",
+ name: AppRoutes.overview.name,
+ builder: (context, state) {
+ return const OverviewPage();
+ },
+ routes: [
+ GoRoute(
+ path: "profile",
+ name: AppRoutes.profile.name,
+ builder: (context, state) {
+ return BlocProvider<ProfileCubit>(
+ create: (BuildContext context) => ProfileCubit(
+ profileRepository: locator<ProfileRepository>(),
+ authenticationRepository:
+ locator<AuthenticationRepository>())
+ ..init(),
+ child: const ProfilePage(), //mobileKey: key);
+ );
},
routes: [
GoRoute(
- path: "/${AppRoutes.myBooking.name}",
- name: AppRoutes.myBooking.name,
- builder: (context, state) {
- return MyBookingPage(booking: state.extra as Booking);
- }),
- GoRoute(
- path: "/${AppRoutes.payMyBooking.name}",
- name: AppRoutes.payMyBooking.name,
+ path: "profile_settings",
+ name: AppRoutes.profileSettings.name,
builder: (context, state) {
- return const MyBookingPaymentPage();
- }),
- ]),
- GoRoute(
- path: "/${AppRoutes.paymentCards.name}",
- name: AppRoutes.paymentCards.name,
- builder: (context, state) {
- return BlocProvider(
- create: (context) => PaymentCardsCubit(),
- child: Builder(builder: (context) {
- final scaffold =
- state.uri.queryParameters[needsScaffold] == 'true';
- return PaymentCardsPage(needScaffold: scaffold);
- }));
- }),
- GoRoute(
- path: "/login",
- name: AppRoutes.login.name,
- builder: (context, state) {
- final queryForced = state.uri.queryParameters['forced'] ?? 'false';
- final forced = bool.parse(queryForced);
- return BlocProvider(
- create: (context) => LoginCubit(
- authRepository: locator.get(),
- forced: forced,
- ),
- child: const LoginPage());
- },
- ),
- GoRoute(
- path: "/redeem",
- name: AppRoutes.redeem.name,
- builder: (context, state) => const RedeemPage(),
- ),
- GoRoute(
- path: "/${AppRoutes.preregistration.name}",
- name: AppRoutes.preregistration.name,
+ return BlocProvider<ProfileSettingsCubit>(
+ create: (BuildContext context) => ProfileSettingsCubit(
+ profileRepository: locator<ProfileRepository>(),
+ profileSettingsRepository:
+ locator<ProfileSettingsRepository>(),
+ authenticationRepository:
+ locator<AuthenticationRepository>())
+ ..init(),
+ child: const ProfileSettingsPage(),
+ );
+ },
+ routes: [
+ GoRoute(
+ path: 'change_password',
+ name: AppRoutes.changePassword.name,
+ builder: (context, state) {
+ return const ChangePasswordModal();
+ })
+ ]),
+ ],
+ ),
+ GoRoute(
+ path: AppRoutes.pastCancelledBookings.name,
+ name: AppRoutes.pastCancelledBookings.name,
+ builder: (context, state) {
+ final extras = state.extra as List<dynamic>;
+ Booking booking = extras[0] as Booking;
+ bool isCancelled = extras[1] as bool;
+ return PastCancelledBookingDetailPage(
+ booking: booking, isCancelled: isCancelled);
+ }),
+ GoRoute(
+ path: AppRoutes.bookingDetails.name,
+ name: AppRoutes.bookingDetails.name,
builder: (context, state) {
- final extras = state.extra as List<dynamic>;
- final booking = extras[0] as Booking;
- final upSales = extras[1] as UpSales;
- return BlocProvider(
- create: (context) =>
- PreregistrationCubit(booking: booking, upSales: upSales)
- ..init(),
- child: const PreregistrationFlow(),
+ final booking = state.extra as Booking;
+ return BlocProvider<BookingDetailsBloc>(
+ create: (BuildContext context) => BookingDetailsBloc(booking,
+ bookingDetailsRepository:
+ locator<BookingDetailsRepository>(),
+ profileRepository: locator<ProfileRepository>(),
+ upSaleRepository: locator<UpSalesRepository>(),
+ houseKeepingRepository: locator<HouseKeepingRepository>()),
+ child: const BookingDetailsPage(),
);
- }),
- GoRoute(
- path: "/${AppRoutes.houseKeeping.name}",
- name: AppRoutes.houseKeeping.name,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return HousekeepingPage(booking: booking);
- },
- ),
- GoRoute(
- path: "/${AppRoutes.checkOut.name}",
- name: AppRoutes.checkOut.name,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return BlocProvider(
- create: (context) => CheckoutCubit(booking,
- locator<CheckOutRepository>(), context.read<PaymentCubit>())
- ..init(),
- child: BlocBuilder<CheckoutCubit, CheckoutState>(
- builder: (context, state) {
- return CheckOutFlow(key: ValueKey(state));
- }));
- },
- ),
- GoRoute(
- path: "/${AppRoutes.paymentProcessing.name}",
- name: AppRoutes.paymentProcessing.name,
- builder: (context, state) => const PaymentProcessingPage(),
- ),
- GoRoute(
- path: "/${AppRoutes.notifications.name}",
- name: AppRoutes.notifications.name,
- builder: (context, state) {
- return BlocProvider(
- create: (context) =>
- NotificationsCubit(locator<NotificationsRepository>())
- ..init(),
- child: const NotificationsPage(
- notificationPermissions: [],
+ },
+ routes: [
+ GoRoute(
+ path: "key",
+ name: AppRoutes.key.name,
+ builder: (context, state) {
+ final roomNumber = state.extra as String? ?? '';
+ return KeyPage(roomNumber: roomNumber);
+ },
),
- );
- },
- ),
- GoRoute(
- path: "/${AppRoutes.checkOutSuccess.name}",
- name: AppRoutes.checkOutSuccess.name,
- builder: (context, state) {
- final digitalCard = state.extra as bool;
- return CheckOutSuccessPage(digitalCard: digitalCard);
- },
- ),
- GoRoute(
- path: "/${AppRoutes.checkOutError.name}",
- name: AppRoutes.checkOutError.name,
- builder: (context, state) => const CheckOutErrorPage(),
- ),
- ShellRoute(
- pageBuilder: (context, state, child) {
- return CustomTransitionPage<void>(
- key: state.pageKey,
- child: BlocProvider(
- create: (_) {
- final extras = state.extra as List<dynamic>;
-
- return UpSalesCubit(
- upSaleRepository: locator<UpSalesRepository>(),
- booking: extras[0] as Booking,
- )..init();
- },
- child: child,
- ),
+ ],
+ ),
+ ],
+ ),
+ ShellRoute(
+ pageBuilder: (context, state, child) {
+ return CustomTransitionPage(
transitionsBuilder:
(context, animation, secondaryAnimation, child) {
return SlideTransition(
@@ -457,124 +347,300 @@ final router = GoRouter(
child: child,
);
},
- transitionDuration: const Duration(milliseconds: 250),
- );
- },
- routes: [
- GoRoute(
- path: "/${AppRoutes.upSalesCatalog.name}",
- name: AppRoutes.upSalesCatalog.name,
- builder: (context, state) {
- return const UpSalesCatalog();
- },
- ),
- GoRoute(
- path: "/${AppRoutes.roomUpgrade.name}",
- name: AppRoutes.roomUpgrade.name,
- builder: (context, state) {
- return RoomUpgradePage(
- roomUpgradeList: state.extra as RoomUpgradeList);
- },
- ),
- GoRoute(
- path: "/${AppRoutes.addonUpgrade.name}",
- name: AppRoutes.addonUpgrade.name,
- builder: (context, state) {
- final extras = state.extra as List<dynamic>;
- return AddonUpgradePage(
- addonUpgrade: extras[0] as AddOnUpgrade,
- extrasTotalPrice: extras[1] as int,
- selectedRoomUpgrade: extras[2] as String,
- );
- }),
- GoRoute(
- path: "/${AppRoutes.servicesUpgrade.name}",
- name: AppRoutes.servicesUpgrade.name,
- builder: (context, state) {
- return ServicesUpgradePage(
- roomUpgradeList: state.extra as RoomUpgradeList);
- },
- ),
- GoRoute(
- path: "/${AppRoutes.upSaleConfirmation.name}",
- name: AppRoutes.upSaleConfirmation.name,
+ child: BlocProvider(
+ create: (context) => MyBookingCubit(
+ locator<MyBookingRepository>(),
+ context.read<PaymentCubit>(),
+ initialBooking: state.extra as Booking)
+ ..init(),
+ child: child));
+ },
+ routes: [
+ GoRoute(
+ path: "/${AppRoutes.myBooking.name}",
+ name: AppRoutes.myBooking.name,
builder: (context, state) {
- final extras = state.extra as List<dynamic>;
- final selectedUpSales = extras[0] as List<AddOnUpgrade?>;
- final extrasTotalPrice = extras[1] as int;
- final selectedRoomUpgrade = extras[2] as RoomUpgrade?;
- return UpSaleConfirmationPage(
- selectedUpSales:
- selectedUpSales.whereType<AddOnUpgrade>().toList(),
- extrasTotalPrice: extrasTotalPrice,
- selectedRoomUpgrade: selectedRoomUpgrade);
- },
- ),
- GoRoute(
- path: "/${AppRoutes.upSalesProcessing.name}",
- name: AppRoutes.upSalesProcessing.name,
+ return MyBookingPage(booking: state.extra as Booking);
+ }),
+ GoRoute(
+ path: "/${AppRoutes.payMyBooking.name}",
+ name: AppRoutes.payMyBooking.name,
builder: (context, state) {
- final cubit = context.read<UpSalesCubit>();
- cubit.addUpSalesToBooking();
-
- return BlocBuilder<UpSalesCubit, UpSalesState>(
- builder: (context, state) {
- return UpSalesProcessingPage(key: ValueKey(state));
- });
- },
- ),
- GoRoute(
- path: "/${AppRoutes.upSalesError.name}",
- name: AppRoutes.upSalesError.name,
- builder: (context, state) => const UpSalesErrorPage(),
- ),
- ]),
- GoRoute(
- path: "/${AppRoutes.chooseShareRoom.name}",
- name: AppRoutes.chooseShareRoom.name,
+ return const MyBookingPaymentPage();
+ }),
+ ]),
+ GoRoute(
+ path: "/${AppRoutes.paymentCards.name}",
+ name: AppRoutes.paymentCards.name,
builder: (context, state) {
- final booking = state.extra as Booking;
return BlocProvider(
- create: (context) =>
- ChooseShareRoomCubit(locator<ChooseShareRoomRepository>())
- ..init(),
- child: ChooseShareRoomPage(booking: booking),
- );
- },
- ),
- GoRoute(
- path: "/${AppRoutes.roomInfo.name}",
- name: AppRoutes.roomInfo.name,
+ create: (context) => PaymentCardsCubit(),
+ child: Builder(builder: (context) {
+ final scaffold =
+ state.uri.queryParameters[needsScaffold] == 'true';
+ return PaymentCardsPage(needScaffold: scaffold);
+ }));
+ }),
+ GoRoute(
+ path: "/login",
+ name: AppRoutes.login.name,
+ builder: (context, state) {
+ final queryForced = state.uri.queryParameters['forced'] ?? 'false';
+ final forced = bool.parse(queryForced);
+ return BlocProvider(
+ create: (context) => LoginCubit(
+ authRepository: locator.get(),
+ forced: forced,
+ ),
+ child: const LoginPage());
+ },
+ ),
+ GoRoute(
+ path: "/redeem",
+ name: AppRoutes.redeem.name,
+ builder: (context, state) => const RedeemPage(),
+ ),
+ GoRoute(
+ path: "/${AppRoutes.preregistration.name}",
+ name: AppRoutes.preregistration.name,
builder: (context, state) {
final extras = state.extra as List<dynamic>;
final booking = extras[0] as Booking;
- final isAssigned = extras[1] as bool;
return BlocProvider(
create: (context) =>
- ChooseShareRoomCubit(locator<ChooseShareRoomRepository>()),
- child: RoomInfoPage(booking: booking, isAssigned: isAssigned),
+ PreregistrationCubit(booking: booking)..init(),
+ child: const PreregistrationFlow(),
);
- },
- ),
- GoRoute(
- path: "/${AppRoutes.shareRoom.name}",
- name: AppRoutes.shareRoom.name,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return BlocProvider(
- create: (context) =>
- ChooseShareRoomCubit(locator<ChooseShareRoomRepository>()),
- child: ShareRoomPage(booking: booking),
+ }),
+ GoRoute(
+ path: "/${AppRoutes.houseKeeping.name}",
+ name: AppRoutes.houseKeeping.name,
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return HousekeepingPage(booking: booking);
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.checkOut.name}",
+ name: AppRoutes.checkOut.name,
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return BlocProvider(
+ create: (context) => CheckoutCubit(booking,
+ locator<CheckOutRepository>(), context.read<PaymentCubit>())
+ ..init(),
+ child: BlocBuilder<CheckoutCubit, CheckoutState>(
+ builder: (context, state) {
+ return CheckOutFlow(key: ValueKey(state));
+ }));
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.paymentProcessing.name}",
+ name: AppRoutes.paymentProcessing.name,
+ builder: (context, state) => const PaymentProcessingPage(),
+ ),
+ GoRoute(
+ path: "/${AppRoutes.notifications.name}",
+ name: AppRoutes.notifications.name,
+ builder: (context, state) {
+ return BlocProvider(
+ create: (context) =>
+ NotificationsCubit(locator<NotificationsRepository>())..init(),
+ child: const NotificationsPage(
+ notificationPermissions: [],
+ ),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.checkOutSuccess.name}",
+ name: AppRoutes.checkOutSuccess.name,
+ builder: (context, state) {
+ final digitalCard = state.extra as bool;
+ return CheckOutSuccessPage(digitalCard: digitalCard);
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.checkOutError.name}",
+ name: AppRoutes.checkOutError.name,
+ builder: (context, state) => const CheckOutErrorPage(),
+ ),
+ ShellRoute(
+ pageBuilder: (context, state, child) {
+ return CustomTransitionPage<void>(
+ key: state.pageKey,
+ child: BlocProvider(
+ create: (_) {
+ final extras = state.extra as List<dynamic>;
+
+ return UpSalesCubit(
+ upSaleRepository: locator<UpSalesRepository>(),
+ booking: extras[0] as Booking,
+ )..init();
+ },
+ child: child,
+ ),
+ transitionsBuilder:
+ (context, animation, secondaryAnimation, child) {
+ return SlideTransition(
+ position: Tween<Offset>(
+ begin: const Offset(1.0, 0.0),
+ end: Offset.zero,
+ ).animate(CurvedAnimation(
+ parent: animation,
+ curve: Curves.easeInOut,
+ )),
+ child: child,
+ );
+ },
+ transitionDuration: const Duration(milliseconds: 250),
);
},
- ),
- GoRoute(
- path: "/${AppRoutes.forceUpdate.name}",
- name: AppRoutes.forceUpdate.name,
- builder: (context, state) {
- return const ForceUpdatePage();
- }),
- /* GoRoute(
+ routes: [
+ GoRoute(
+ path: "/${AppRoutes.upSalesCatalog.name}",
+ name: AppRoutes.upSalesCatalog.name,
+ builder: (context, state) {
+ return const UpSalesCatalog();
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.roomUpgrade.name}",
+ name: AppRoutes.roomUpgrade.name,
+ builder: (context, state) {
+ return RoomUpgradePage(
+ roomUpgradeList: state.extra as RoomUpgradeList);
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.addonUpgrade.name}",
+ name: AppRoutes.addonUpgrade.name,
+ builder: (context, state) {
+ final extras = state.extra as List<dynamic>;
+ return AddonUpgradePage(
+ addonUpgrade: extras[0] as AddOnUpgrade,
+ extrasTotalPrice: extras[1] as int,
+ selectedRoomUpgrade: extras[2] as String,
+ );
+ }),
+ GoRoute(
+ path: "/${AppRoutes.servicesUpgrade.name}",
+ name: AppRoutes.servicesUpgrade.name,
+ builder: (context, state) {
+ return ServicesUpgradePage(
+ roomUpgradeList: state.extra as RoomUpgradeList);
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.upSaleConfirmation.name}",
+ name: AppRoutes.upSaleConfirmation.name,
+ builder: (context, state) {
+ final extras = state.extra as List<dynamic>;
+ final selectedUpSales = extras[0] as List<AddOnUpgrade?>;
+ final extrasTotalPrice = extras[1] as int;
+ final selectedRoomUpgrade = extras[2] as RoomUpgrade?;
+ return UpSaleConfirmationPage(
+ selectedUpSales:
+ selectedUpSales.whereType<AddOnUpgrade>().toList(),
+ extrasTotalPrice: extrasTotalPrice,
+ selectedRoomUpgrade: selectedRoomUpgrade);
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.upSalesProcessing.name}",
+ name: AppRoutes.upSalesProcessing.name,
+ builder: (context, state) {
+ final cubit = context.read<UpSalesCubit>();
+ cubit.addUpSalesToBooking();
+
+ return BlocBuilder<UpSalesCubit, UpSalesState>(
+ builder: (context, state) {
+ return UpSalesProcessingPage(key: ValueKey(state));
+ });
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.upSalesError.name}",
+ name: AppRoutes.upSalesError.name,
+ builder: (context, state) => const UpSalesErrorPage(),
+ ),
+ ]),
+ GoRoute(
+ path: "/${AppRoutes.chooseShareRoom.name}",
+ name: AppRoutes.chooseShareRoom.name,
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return BlocProvider(
+ create: (context) =>
+ ChooseShareRoomCubit(locator<ChooseShareRoomRepository>())
+ ..init(),
+ child: ChooseShareRoomPage(booking: booking),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.roomInfo.name}",
+ name: AppRoutes.roomInfo.name,
+ builder: (context, state) {
+ final extras = state.extra as List<dynamic>;
+ final booking = extras[0] as Booking;
+ final isAssigned = extras[1] as bool;
+ return BlocProvider(
+ create: (context) =>
+ ChooseShareRoomCubit(locator<ChooseShareRoomRepository>()),
+ child: RoomInfoPage(booking: booking, isAssigned: isAssigned),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.shareRoom.name}",
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return BlocProvider(
+ create: (context) =>
+ ChooseShareRoomCubit(locator<ChooseShareRoomRepository>()),
+ child: ShareRoomPage(booking: booking),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.receivedSharedRoom.name}",
+ name: AppRoutes.receivedSharedRoom.name,
+ builder: (context, state) {
+ final sharingId = state.uri.queryParameters['sharingId'];
+ final hotelCode = state.uri.queryParameters['hotelCode'];
+ debugPrint("Received shared room: $sharingId $hotelCode ${state.uri}");
+ return BlocProvider(
+ create: (context) =>
+ ReceivedSharedRoomCubit(sharingId ?? '', hotelCode ?? '')
+ ..init(),
+ child: const ReceivedSharedRoomPage(),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.receivedSharedBooking.name}",
+ name: AppRoutes.receivedSharedBooking.name,
+ builder: (context, state) {
+ final sharingId = state.uri.queryParameters['sharingId'];
+ final hotelCode = state.uri.queryParameters['hotelCode'];
+ debugPrint("Received shared booking: $sharingId $hotelCode ${state.uri}");
+ return BlocProvider(
+ create: (context) =>
+ ReceivedSharedBookingCubit(sharingId ?? '', hotelCode ?? '')
+ ..init(),
+ child: const ReceivedSharedBookingPage(),
+ );
+ },
+ ),
+ GoRoute(
+ path: "/${AppRoutes.forceUpdate.name}",
+ name: AppRoutes.forceUpdate.name,
+ builder: (context, state) {
+ return const ForceUpdatePage();
+ }),
+ /* GoRoute(
path: "/keys",
name: AppRoutes.keys.name,
builder: (context, state) {
@@ -582,7 +648,7 @@ final router = GoRouter(
return KeyPage(List<mobileKey>: keys);
},
) */
- /*
+ /*
GoRoute(
path: "/login",
name: AppRoutes.login.name,
@@ -615,4 +681,4 @@ final router = GoRouter(
transitionDuration: const Duration(milliseconds: 1000),
);
}) */
- ]);
+ ]);
diff --git a/comwell_key_app/lib/routing/app_routes.dart b/comwell_key_app/lib/routing/app_routes.dart
index 701126da..dcb2cd90 100644
--- a/comwell_key_app/lib/routing/app_routes.dart
+++ b/comwell_key_app/lib/routing/app_routes.dart
@@ -1,4 +1,6 @@
enum AppRoutes {
+ receivedSharedRoom,
+ receivedSharedBooking,
welcome,
initial,
redeem,
@@ -37,6 +39,7 @@ enum AppRoutes {
shareRoom,
forceUpdate,
upSalesError,
+ sharedbooking,
payMyBooking,
paymentProcessing,
checkOutSuccess,
diff --git a/comwell_key_app/lib/services/api.dart b/comwell_key_app/lib/services/api.dart
index 8ba96dbb..5dfd59fa 100644
--- a/comwell_key_app/lib/services/api.dart
+++ b/comwell_key_app/lib/services/api.dart
@@ -1,12 +1,13 @@
import 'dart:convert';
+import 'dart:io';
import 'package:comwell_key_app/hotel_information/models/hotel.dart';
import 'package:comwell_key_app/housekeeping/models/housekeeping.dart';
import 'package:comwell_key_app/notifications/models/notification_permission.dart';
-import 'package:comwell_key_app/profile_settings/model/user.dart';
+import 'package:comwell_key_app/pregistration/prereg_request_model.dart';
import 'package:comwell_key_app/services/adyen/payment_method.dart';
import 'package:comwell_key_app/services/http_client.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
-import 'package:comwell_key_app/services/models/bookings_dto.dart';
+import 'package:comwell_key_app/services/models/simple_user_dto.dart';
import 'package:comwell_key_app/services/models/user_dto.dart';
import 'package:comwell_key_app/services/utils/api_endpoints.dart';
import 'package:comwell_key_app/up_sales/models/addon_list.dart';
@@ -17,6 +18,7 @@ import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:easy_localization/easy_localization.dart';
+import 'package:package_info_plus/package_info_plus.dart';
import 'adyen/stored_payment_methods_response.dart';
@@ -73,6 +75,7 @@ class Api {
final json = jsonEncode(body);
final response = await dio
.get<List<dynamic>>(ApiEndpoints.getCurrentBookings, data: json);
+
return response.data!.map((e) => BookingDTO.fromJson(e as Json)).toList();
}
@@ -81,9 +84,10 @@ class Api {
return StoredPaymentsResponse.fromJson(response.data!);
}
- Future<dynamic> createAdyenSession(String bookingId, bool usePoints) async {
+ Future<dynamic> createAdyenSession(String bookingId, bool usePoints, String hotelCode) async {
final body = {
- "bookingConfirmationId": bookingId,
+ "hotelCode": hotelCode,
+ "bookingConfirmationNumber": bookingId,
"returnUrl": ApiEndpoints.returnUrl,
"usePoints": usePoints,
};
@@ -143,7 +147,7 @@ class Api {
};
data.addAll(paymentResult);
data.remove("storePaymentMethod");
- print("Data: $data");
+ debugPrint("Data: $data");
final response = await dio.post<Json>(ApiEndpoints.adyenPayments,
data: data,
options: Options(
@@ -161,69 +165,62 @@ class Api {
return dio.delete<void>(ApiEndpoints.deleteGuest);
}
- Future<Response<dynamic>> signupForComwellClub(User user) async {
- final zipCode = user.address.zipCode;
- final body = {"gender": user.gender, "addressZip": zipCode};
- final json = jsonEncode(body);
+ Future<Response<dynamic>> signupForComwellClub() async {
final response =
- await dio.post<dynamic>(ApiEndpoints.clubSignup, data: json);
+ await dio.post<dynamic>(ApiEndpoints.clubSignup);
return response;
}
- Future<Response<dynamic>> preRegistration(String confirmationId) async {
- final body = {
- "confirmationId": confirmationId,
- };
- final json = jsonEncode(body);
-
+ Future<Response<dynamic>> preRegistration(PreregRequestDto request) async {
+
+ final json = jsonEncode(request);
+ debugPrint("json: $json");
final response =
await dio.post<dynamic>(ApiEndpoints.preRegistration, data: json);
return response;
}
- Future<Response<dynamic>> getNotificationPermissions(int guestId) async {
- final body = {
- "permissions": [
- {"id": 0, "allowed": false},
- ]
- };
-
- final response = await dio.put<dynamic>(
- '${ApiEndpoints.communicationPreference}/$guestId/communication-preference',
- data: body,
- options: Options(
- headers: {
- 'Content-Type': 'application/json',
- },
- ),
+ Future<Response<dynamic>> getNotificationPermissions() async {
+ final response = await dio.get<dynamic>(
+ '${ApiEndpoints.communicationPreference}?permissionAreas=club',
);
return response;
}
- Future<dynamic> updateNotificationPreferences(
- int guestId, Iterable<NotificationPermission> notificationPermissions) {
+ Future<BookingDTO> findBookingByConfirmationId(
+ String confirmationId, String lastName) async {
final body = {
- "permissions": notificationPermissions
- .map((permission) =>
- {"id": permission.id, "allowed": permission.allowed})
- .toList()
+ "confirmationNumber": confirmationId,
+ "lastName": lastName,
};
- final json = jsonEncode(body);
- final response = dio.put<void>(
- '${ApiEndpoints.communicationPreference}/$guestId/communication-preference',
- data: json);
- return response;
+
+ final data = jsonEncode(body);
+ final response = await dio
+ .post<Json>(ApiEndpoints.findBookingByConfirmationId, data: data);
+ return BookingDTO.fromJson(response.data!);
+ }
+
+ Future<dynamic> updateNotificationPreferences(
+ Iterable<NotificationPermission> notificationPermissions) {
+ final permissions = {
+ for (var permission in notificationPermissions)
+ permission.code.toString(): permission.given
+ };
+ final simpleUserDto = SimpleUserDto(permissions: permissions);
+ return updateUser(simpleUserDto);
}
- Future<dynamic> updateUser(UserDto updatedUser) {
+ Future<dynamic> updateUser(SimpleUserDto updatedUser) {
final json = jsonEncode(updatedUser);
+ debugPrint("Updated user: $json");
final response = dio.put<void>(
ApiEndpoints.updateGuestData,
data: json,
);
return response;
}
+
Future<dynamic> getHotelInfo(String hotelCode) async {
final cultureString = _currentLocale.toString().replaceAll('_', '-');
@@ -233,9 +230,9 @@ class Api {
return response;
}
- Future<Json?> checkIn(String confirmationId) async {
+ Future<Json?> checkIn(String confirmationNumber) async {
final body = {
- "confirmationId": confirmationId,
+ "confirmationNumber": confirmationNumber,
};
final data = jsonEncode(body);
@@ -244,9 +241,9 @@ class Api {
return response.data;
}
- Future<Json?> checkOut(String confirmationId) async {
+ Future<Json?> checkOut(String confirmationNumber) async {
final body = {
- "confirmationId": confirmationId,
+ "confirmationNumber": confirmationNumber,
};
final data = jsonEncode(body);
final response = await dio.post<Json>(ApiEndpoints.checkOut, data: data);
@@ -309,27 +306,27 @@ class Api {
return json["invitationCode"]! as String;
}
- Future<void> provisionKey(String bookingId) async {
+ Future<void> provisionKey(String confirmationNumber, String hotelCode) async {
await dio
- .post<void>(ApiEndpoints.provisionKey, data: {'bookingId': bookingId});
+ .post<void>(ApiEndpoints.provisionKey, data: {'confirmationNumber': confirmationNumber, 'hotelCode': hotelCode});
}
Future<BookingDTO?> getBookingDetails(
- String hmsConfirmationNumber, String hotelCode) async {
+ String confirmationNumber, String hotelCode) async {
final uri = Uri.parse(ApiEndpoints.getBookingDetails).replace(
queryParameters: {
- 'confirmationId': hmsConfirmationNumber,
+ 'confirmationNumber': confirmationNumber,
'hotelCode': hotelCode,
},
);
final response = await dio.get<Json>(uri.toString());
-
+ debugPrint("Response: ${response.data}");
return BookingDTO.fromJson(response.data!);
}
- Future<Json> postRoomSelection(String bookingId, String userId) async {
+ Future<Json> postRoomSelection(String confirmationNumber, String userId) async {
final body = {
- "bookingConfirmationId": bookingId,
+ "confirmationNumber": confirmationNumber,
"userId": userId,
};
final data = jsonEncode(body);
@@ -339,9 +336,9 @@ class Api {
}
Future<UpSalesDTO> fetchUpSales(
- String confirmationId, String hotelCode) async {
+ String confirmationNumber, String hotelCode) async {
final body = {
- "confirmationId": confirmationId,
+ "confirmationNumber": confirmationNumber,
"property": hotelCode,
"culture": _currentLocale.toString().replaceAll('_', '-'),
};
@@ -350,10 +347,10 @@ class Api {
return UpSalesDTO.fromJson(response.data!);
}
- Future<void> addUpSalesToBooking(String confirmationId, String hotelCode,
+ Future<void> addUpSalesToBooking(String confirmationNumber, String hotelCode,
String roomType, List<AddOnList> selectedUpSales) async {
final body = {
- "confirmationNumber": confirmationId,
+ "confirmationNumber": confirmationNumber,
"property": hotelCode,
"roomType": roomType,
"addOnList": selectedUpSales,
@@ -362,4 +359,90 @@ class Api {
await dio.post<void>(ApiEndpoints.addUpSalesToBooking, data: data);
return;
}
+
+ Future<void> removeGuestsFromBooking(
+ String confirmationNumber, String hotelCode, int guestId) async {
+ final body = {
+ "hotelCode": hotelCode,
+ "confirmationNumber": confirmationNumber,
+ "personId": guestId,
+ };
+ final data = jsonEncode(body);
+ debugPrint("Data: $data");
+ await dio.post<void>(ApiEndpoints.removeGuestsFromBooking, data: data);
+ }
+
+ Future<String> createRoomSharingLink(
+ String confirmationNumber, String hotelCode, int sharingType) async {
+ final body = {
+ "confirmationNumber": confirmationNumber,
+ "hotelCode": hotelCode,
+ "sharingType": sharingType,
+ };
+ final data = jsonEncode(body);
+ final response =
+ await dio.post<String>(ApiEndpoints.createRoomSharingLink, data: data);
+ debugPrint("Response: $response");
+ return response.data!;
+ }
+
+ Future<BookingDTO> consumeRoomSharingLink(
+ String sharingId, String hotelCode) async {
+ final uri = Uri.parse(ApiEndpoints.consumeRoomSharingLink).replace(
+ queryParameters: {
+ 'sharingId': sharingId,
+ 'hotelCode': hotelCode,
+ },
+ );
+ final response = await dio.get<Json>(uri.toString());
+ return BookingDTO.fromJson(response.data!);
+ }
+
+ Future<void> registerDeviceAndProfileSymplify(String token) async {
+ final symplifyUrl = dotenv.env['SYMPLIFY_URL'];
+ final apiKey = dotenv.env['SYMPLIFY_API_KEY'];
+ final customerId = dotenv.env['SYMPLIFY_CUSTOMER_ID'];
+ final packageInfo = await PackageInfo.fromPlatform();
+ final packageName = packageInfo.packageName;
+
+ debugPrint('Package name: $packageName');
+
+ //App type 6 is Firebase
+ //https://apidocs.symplify.com/#233ad620-28d7-4d19-b394-cfcac0ed2a49
+ final profileResponse = await fetchProfileSettings();
+ final profileData = profileResponse.data as Json;
+ final profile = UserDto.fromJson(profileData);
+ String emailAddress = '';
+ String firstName = '';
+ String lastName = '';
+ if (Platform.isIOS) {
+ emailAddress = '${profile.email}IOS-test';
+ firstName = '${profile.firstName}IOS-test';
+ lastName = '${profile.lastName}IOS-test';
+ } else {
+ emailAddress = '${profile.email}Android-test';
+ firstName = '${profile.firstName}Android-test';
+ lastName = '${profile.lastName}Android-test';
+ }
+ final body = {
+ "originalId": emailAddress,
+ "firstName": firstName,
+ "lastName": lastName,
+ "emailAddress": emailAddress,
+ "deviceInfo": [
+ {
+ "devicetoken": token,
+ "appType": "6",
+ },
+ ],
+ };
+ final data = jsonEncode(body);
+ await dio.put<void>('$symplifyUrl/$customerId/apps/$packageName/devices',
+ data: data,
+ options: Options(headers: {
+ 'X-Carma-Authentication-Token': apiKey,
+ 'Content-Type': 'application/json',
+ }));
+ return;
+ }
}
diff --git a/comwell_key_app/lib/services/http_client.dart b/comwell_key_app/lib/services/http_client.dart
index 9482f380..6b5f9b57 100644
--- a/comwell_key_app/lib/services/http_client.dart
+++ b/comwell_key_app/lib/services/http_client.dart
@@ -18,9 +18,9 @@ class HttpClient {
var dio = Dio(
BaseOptions(
baseUrl: dotenv.env['SERVICE_URL'] ?? '',
- receiveTimeout: const Duration(milliseconds: 10000),
- connectTimeout: const Duration(milliseconds: 10000),
- sendTimeout: const Duration(milliseconds: 10000),
+ receiveTimeout: const Duration(milliseconds: 15000),
+ connectTimeout: const Duration(milliseconds: 15000),
+ sendTimeout: const Duration(milliseconds: 15000),
),
);
dio.interceptors.add(ResponseHandleInterceptor(dio));
diff --git a/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart b/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
index 427307d7..99fa300f 100644
--- a/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
+++ b/comwell_key_app/lib/services/interceptors/response_handle_interceptor.dart
@@ -26,7 +26,7 @@ class ResponseHandleInterceptor extends Interceptor {
) async {
final String? accessToken =
await _secureStorageService.read(key: constants.accessToken);
-
+ debugPrint('accessToken: $accessToken');
if (accessToken == null) {
//logout user
options.extra["tokenErrorType"] = TokenErrorType.tokenNotFound;
@@ -49,66 +49,44 @@ class ResponseHandleInterceptor extends Interceptor {
DioException err,
ErrorInterceptorHandler handler,
) async {
- // TEMP: Always treat as 426 for testing force update
-
final response = err.response;
if (response == null) {
+ debugPrint('Error: No response received - ${err.message}');
return handler.next(DioException(
- message: "Missing response \\${err.response}",
- requestOptions: RequestOptions(),
+ message: "No response received: ${err.message}",
+ requestOptions: err.requestOptions,
+ type: err.type,
+ error: err.error,
));
}
+
final statusCode = response.statusCode;
+ debugPrint('HTTP Error: $statusCode on ${response.requestOptions.path}');
+
try {
switch (statusCode) {
case 404:
- final err = DioException(
- requestOptions: response.requestOptions, error: 'Not found');
- handler.next(err);
+ final error = DioException(
+ requestOptions: response.requestOptions,
+ response: response,
+ error: 'Not found: ${response.requestOptions.path}');
+ handler.next(error);
break;
+
case 401:
- retryCount++;
- try {
- final identifier = await _secureStorageService.read(key: constants.identifier);
- final authResult = await _authenticationRepository.acquireTokenSilent(identifier ?? '');
- await _secureStorageService.write(key: constants.accessToken, value: authResult.accessToken);
-
- if (retryCount < 3) {
-
- if (authResult.accessToken.isNotEmpty) {
- // Retry the original request with the new token
- final options = response.requestOptions;
- options.headers['Authorization'] = authResult.accessToken;
- final opts = Options(
- method: response.requestOptions.method,
- headers: response.requestOptions.headers);
- final retryRequest = await _dio.request<dynamic>(
- response.requestOptions.path,
- options: opts,
- data: response.requestOptions.data,
- queryParameters: response.requestOptions.queryParameters);
-
- return handler.resolve(retryRequest);
-
- }
-
- } else {
- retryCount = 0;
- _authenticationRepository.logOut(forced: true);
- return handler.reject(err);
- }
- } catch (e){
- debugPrint('Error acquiring token silently: $e');
- }
+ await _handleUnauthorized(err, handler, response);
break;
- case 500:
- final errorMessage = response.data['detail'] as String;
- final err = DioException(
- requestOptions: response.requestOptions, error: errorMessage);
- handler.next(err);
+ case 500:
+ final errorMessage = _extractErrorMessage(response.data, 'Internal server error');
+ final error = DioException(
+ requestOptions: response.requestOptions,
+ response: response,
+ error: errorMessage);
+ handler.next(error);
break;
+
case 426:
// Navigate to force update page
final navigatorKey = locator<GlobalKey<NavigatorState>>();
@@ -116,26 +94,107 @@ class ResponseHandleInterceptor extends Interceptor {
if (context != null) {
GoRouter.of(context).go('/forceUpdate');
}
-
- final err = DioException(
+ final error = DioException(
requestOptions: response.requestOptions,
+ response: response,
error: 'Update required');
- handler.next(err);
+ handler.next(error);
break;
+
default:
- final err = DioException(
+ final errorMessage = _extractErrorMessage(response.data, 'Unknown error');
+ final error = DioException(
requestOptions: response.requestOptions,
- error:
- 'HTTP request error, status code: \\${response.statusCode} - message: \\${response.data['detail']}');
- handler.next(err);
+ response: response,
+ error: 'HTTP $statusCode: $errorMessage');
+ handler.next(error);
break;
}
- } catch (e) {
- final err = DioException(
+ } catch (e, stackTrace) {
+ debugPrint('Error handling HTTP response: $e');
+ debugPrintStack(stackTrace: stackTrace);
+ final error = DioException(
requestOptions: response.requestOptions,
- error: 'Failed to handle the response: \\$e');
- handler.next(err);
+ response: response,
+ error: 'Failed to handle response (HTTP $statusCode): $e');
+ handler.next(error);
+ }
+ }
+
+ Future<void> _handleUnauthorized(
+ DioException originalError,
+ ErrorInterceptorHandler handler,
+ Response<dynamic> response,
+ ) async {
+ retryCount++;
+ debugPrint('401 Unauthorized - Attempt $retryCount/3 to refresh token');
+
+ if (retryCount >= 3) {
+ debugPrint('Max retry attempts reached. Forcing logout.');
+ retryCount = 0;
+ await _authenticationRepository.logOut(forced: true);
+ return handler.reject(originalError);
+ }
+
+ try {
+ final identifier = await _secureStorageService.read(key: constants.identifier);
+
+ if (identifier == null || identifier.isEmpty) {
+ debugPrint('No identifier found. Cannot refresh token. Forcing logout.');
+ retryCount = 0;
+ await _authenticationRepository.logOut(forced: true);
+ return handler.reject(originalError);
+ }
+
+ debugPrint('Attempting silent token acquisition...');
+ final authResult = await _authenticationRepository.acquireTokenSilent(identifier);
+
+ if (authResult.accessToken.isEmpty) {
+ debugPrint('Received empty token. Forcing logout.');
+ retryCount = 0;
+ await _authenticationRepository.logOut(forced: true);
+ return handler.reject(originalError);
+ }
+
+ await _secureStorageService.write(key: constants.accessToken, value: authResult.accessToken);
+ debugPrint('Token refreshed successfully. Retrying request...');
+
+ // Retry the original request with the new token
+ final opts = Options(
+ method: response.requestOptions.method,
+ headers: {
+ ...response.requestOptions.headers,
+ 'Authorization': authResult.accessToken,
+ },
+ );
+
+ final retryRequest = await _dio.request<dynamic>(
+ response.requestOptions.path,
+ options: opts,
+ data: response.requestOptions.data,
+ queryParameters: response.requestOptions.queryParameters,
+ );
+
+ return handler.resolve(retryRequest);
+ } catch (e, stackTrace) {
+ debugPrint('Error acquiring token silently: $e');
+ debugPrintStack(stackTrace: stackTrace);
+ retryCount = 0;
+ await _authenticationRepository.logOut(forced: true);
+ return handler.reject(originalError);
+ }
+ }
+
+ String _extractErrorMessage(dynamic data, String fallback) {
+ if (data == null) return fallback;
+ if (data is String) return data;
+ if (data is Map) {
+ return data['detail']?.toString() ??
+ data['message']?.toString() ??
+ data['error']?.toString() ??
+ fallback;
}
+ return fallback;
}
@override
diff --git a/comwell_key_app/lib/services/mappers/booking_mapper.dart b/comwell_key_app/lib/services/mappers/booking_mapper.dart
index 920e77ef..eee6f5ee 100644
--- a/comwell_key_app/lib/services/mappers/booking_mapper.dart
+++ b/comwell_key_app/lib/services/mappers/booking_mapper.dart
@@ -7,12 +7,17 @@ import 'package:comwell_key_app/services/mappers/room_mapper.dart';
//TODO: Fix actual image
extension BookingDTOMapper on BookingDTO {
- Booking toBooking(int userId, List<Room> rooms) {
+ Booking toBooking() {
final startDate = DateTime.parse(dayIn);
- final endDate = DateTime.parse(dayOut!);
+ final endDate = DateTime.parse(dayOut);
+ final mappedGuests = guests.map((guest) => Guest(
+ firstName: guest.firstName,
+ lastName: guest.lastName,
+ id: guest.id,
+ ));
return Booking(
id: confirmationNumber,
- confirmationId: confirmationNumber,
+ confirmationNumber: confirmationNumber,
roomNumber: roomNumber ?? "",
startDate: startDate,
endDate: endDate,
@@ -21,43 +26,54 @@ extension BookingDTOMapper on BookingDTO {
hotelName: "Hotel $hotelCode",
roomType: roomType ?? '',
addOnItems: addOnItems,
- hmsConfirmationNumber: hmsConfirmationNumber,
- totalCharge: totalCharge ?? 0,
+
+ balance: balance ?? 0,
children: children,
- booker: Guest(name: "$firstName $lastName", id: "$userId"),
+ firstName: firstName,
+ lastName: lastName,
+ bookerFirstName: bookerFirstName,
+ bookerLastName: bookerLastName,
adults: adults,
hotelCode: hotelCode,
bookingDate: startDate,
digitalCard: true,
- balance: balance,
- maskedCardNumber: maskedCardNumber,
- rooms: rooms,
+ isPrimaryGuest: false,
+ maskedCardNumber: null,
+ guests: mappedGuests,
);
}
}
extension BookingMapper on Booking {
BookingDTO toDTO() {
+ final firstName = this.firstName;
+ final lastName = this.lastName;
+ final mappedGuests = guests.map((guest) => GuestDTO(
+ id: guest.id,
+ firstName: guest.firstName,
+ lastName: guest.lastName,
+ )).toList();
return BookingDTO(
roomNumber: roomNumber,
hotelCode: hotelCode,
- firstName: booker.name,
- lastName: booker.name,
- confirmationNumber: id,
- hmsConfirmationNumber: hmsConfirmationNumber,
+ firstName: firstName,
+ lastName: lastName,
+ bookerFirstName: firstName,
+ bookerLastName: lastName,
+ guests: mappedGuests,
+ confirmationNumber: confirmationNumber,
dayIn: startDate.toIso8601String(),
dayOut: endDate.toIso8601String(),
- cancelTime: "",
+ cancelTime: null,
isCancelled: false,
bookTime: startDate.toIso8601String(),
status: reservationStatus.name,
roomType: roomType,
adults: adults,
children: children,
- totalCharge: 200,
- balance: 0,
- addOnItems: addOnItems ?? [],
- maskedCardNumber: "1234567890");
+ balance: balance ?? 0,
+ isPrimaryGuest: false,
+ addOnItems: addOnItems ?? []);
}
}
@@ -65,26 +81,27 @@ extension BookingWithRoomsMapper on Booking {
Booking withRooms(List<Room> rooms) {
return Booking(
id: id,
- confirmationId: confirmationId,
+ confirmationNumber: confirmationNumber,
roomNumber: roomNumber,
startDate: startDate,
endDate: endDate,
- hmsConfirmationNumber: hmsConfirmationNumber,
reservationStatus: reservationStatus,
image: image,
hotelName: hotelName,
roomType: roomType,
addOnItems: addOnItems,
- totalCharge: totalCharge,
+ balance: balance,
children: children,
- booker: booker,
+ firstName: firstName,
+ lastName: lastName,
+ bookerFirstName: bookerFirstName,
+ bookerLastName: bookerLastName,
adults: adults,
hotelCode: hotelCode,
bookingDate: bookingDate,
digitalCard: digitalCard,
- balance: balance,
+ isPrimaryGuest: isPrimaryGuest,
maskedCardNumber: maskedCardNumber,
- rooms: rooms,
);
}
}
@@ -94,8 +111,8 @@ extension BookingWithRoomsMapper on Booking {
}
extension ListBookingMapper on Iterable<BookingDTO> {
- Iterable<Booking> toBookings(int userId, List<Room> rooms) =>
- map((dto) => dto.toBooking(userId, rooms));
+ Iterable<Booking> toBookings() =>
+ map((dto) => dto.toBooking());
}
extension RoomTypeMapper on Booking {
diff --git a/comwell_key_app/lib/services/mappers/bookings_mapper.dart b/comwell_key_app/lib/services/mappers/bookings_mapper.dart
index 3f59f3d6..2e0a3955 100644
--- a/comwell_key_app/lib/services/mappers/bookings_mapper.dart
+++ b/comwell_key_app/lib/services/mappers/bookings_mapper.dart
@@ -1,16 +1,15 @@
import 'package:comwell_key_app/overview/models/bookings.dart';
-import 'package:comwell_key_app/overview/models/room.dart';
import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
import 'package:comwell_key_app/services/models/bookings_dto.dart';
extension BookingsMapper on BookingsDTO {
- Bookings toBookings(int userId, List<Room> rooms) {
+ Bookings toBookings() {
return Bookings(
current:
- current.map((booking) => booking.toBooking(userId, rooms)).toList(),
- past: past.map((booking) => booking.toBooking(userId, rooms)).toList(),
+ current.map((booking) => booking.toBooking()).toList(),
+ past: past.map((booking) => booking.toBooking()).toList(),
cancelled:
- cancelled.map((booking) => booking.toBooking(userId, rooms)).toList(),
+ cancelled.map((booking) => booking.toBooking()).toList(),
);
}
}
diff --git a/comwell_key_app/lib/services/mappers/user_mapper.dart b/comwell_key_app/lib/services/mappers/user_mapper.dart
index c5423e48..d0d34b14 100644
--- a/comwell_key_app/lib/services/mappers/user_mapper.dart
+++ b/comwell_key_app/lib/services/mappers/user_mapper.dart
@@ -1,29 +1,41 @@
import 'package:comwell_key_app/profile_settings/model/address.dart';
import 'package:comwell_key_app/profile_settings/model/user.dart';
+import 'package:comwell_key_app/services/models/simple_user_dto.dart';
import 'package:comwell_key_app/services/models/user_dto.dart';
extension UserMapper on UserDto {
User toUser() {
return User(
id: id,
+ userId: userId,
+ hmsId: hmsId,
firstName: firstName,
lastName: lastName,
email: email,
- addressCountry: addressCountry ?? '',
- phoneNumber: phoneNumber ?? '',
+ emailVerified: emailVerified,
+ addressCountry: addressCountry,
+ phoneNumber: phoneNumber,
address: Address(
- street: addressStreet ?? '',
- city: addressCity ?? '',
- country: addressCountry ?? '',
- zipCode: addressZip ?? '',
+ street: addressStreet,
+ city: addressCity,
+ country: addressCountry,
+ zipCode: addressZip,
),
birthDate: birthDate ?? DateTime.now(),
- shopperReference: clubId ?? '',
- points: points ?? 0,
- gender: gender ?? '',
- clubId: clubId,
- clubLevel: clubLevel,
- clubLevelName: clubLevelName,
+ shopperReference: userId,
+ points: points,
+ gender: gender,
+ isClubMember: isClubMember,
+ createDate: createDate,
+ companyId: companyId,
+ companyName: companyName,
+ symplifyId: symplifyId,
+ signUpDate: signUpDate,
+ signUpCampaign: signUpCampaign,
+ signUpSource: signUpSource,
+ locale: locale,
+ wasRecentlyCreated: wasRecentlyCreated,
+ nationality: nationality,
);
}
}
@@ -32,21 +44,52 @@ extension UserDtoMapper on User {
UserDto toUserDto() {
return UserDto(
id: id,
+ userId: userId,
+ hmsId: hmsId,
firstName: firstName,
lastName: lastName,
email: email,
- clubId: shopperReference,
- clubLevel: clubLevel ?? '',
- clubLevelName: clubLevelName,
+ emailVerified: emailVerified,
+ isClubMember: isClubMember,
birthDate: birthDate,
+ createDate: createDate,
+ companyId: companyId,
+ companyName: companyName,
+ symplifyId: symplifyId,
phoneNumber: phoneNumber,
- gender: gender ?? '',
+ gender: gender,
addressStreet: address.street,
addressZip: address.zipCode,
addressCity: address.city,
- addressCountry: address.country,
+ addressCountry: addressCountry,
points: points,
- locale: 'en',
+ signUpDate: signUpDate,
+ signUpCampaign: signUpCampaign,
+ signUpSource: signUpSource,
+ locale: locale,
+ wasRecentlyCreated: wasRecentlyCreated,
+ permissions: null, // Permissions are not stored in User model
+ nationality: nationality,
+ );
+ }
+}
+
+extension SimpleUserDtoMapper on User {
+ SimpleUserDto toSimpleUserDto() {
+ return SimpleUserDto(
+
+ firstName: firstName,
+ lastName: lastName,
+ birthDate: birthDate.toIso8601String(),
+ phoneNumber: phoneNumber,
+ gender: gender,
+ addressStreet: address.street,
+ addressZip: address.zipCode,
+ addressCity: address.city,
+ addressCountry: addressCountry,
+ locale: locale,
+ permissions: {}, // Permissions are not stored in User model
+
);
}
}
\ No newline at end of file
diff --git a/comwell_key_app/lib/services/models/booking_dto.dart b/comwell_key_app/lib/services/models/booking_dto.dart
index a2710cec..a9176d9b 100644
--- a/comwell_key_app/lib/services/models/booking_dto.dart
+++ b/comwell_key_app/lib/services/models/booking_dto.dart
@@ -10,10 +10,12 @@ class BookingDTO {
final String hotelCode;
final String firstName;
final String lastName;
+ final String bookerFirstName;
+ final String bookerLastName;
+ final List<GuestDTO> guests;
final String confirmationNumber;
- final String hmsConfirmationNumber;
final String dayIn;
- final String? dayOut;
+ final String dayOut;
final String? cancelTime;
final String status;
final bool? isCancelled;
@@ -21,8 +23,8 @@ class BookingDTO {
final String? roomType;
final int adults;
final int children;
- final num? totalCharge;
final num? balance;
+ final bool isPrimaryGuest;
final String? maskedCardNumber;
final List<BookingAddonItem>? addOnItems;
@@ -31,21 +33,23 @@ class BookingDTO {
required this.hotelCode,
required this.firstName,
required this.lastName,
+ this.bookerFirstName = '',
+ this.bookerLastName = '',
+ this.guests = const [],
required this.confirmationNumber,
- required this.hmsConfirmationNumber,
required this.dayIn,
required this.dayOut,
required this.cancelTime,
required this.status,
- required this.isCancelled,
+ this.isCancelled,
required this.bookTime,
required this.roomType,
required this.adults,
required this.children,
- required this.totalCharge,
required this.balance,
- required this.maskedCardNumber,
+ required this.isPrimaryGuest,
this.addOnItems,
+ this.maskedCardNumber,
});
Json toJson() => _$BookingDTOToJson(this);
@@ -53,6 +57,23 @@ class BookingDTO {
factory BookingDTO.fromJson(Json json) => _$BookingDTOFromJson(json);
}
+@JsonSerializable()
+class GuestDTO {
+ final int id;
+ final String firstName;
+ final String lastName;
+
+ GuestDTO({
+ required this.id,
+ required this.firstName,
+ required this.lastName,
+ });
+
+ Json toJson() => _$GuestDTOToJson(this);
+
+ factory GuestDTO.fromJson(Json json) => _$GuestDTOFromJson(json);
+}
+
@JsonSerializable()
class BookingAddonItem {
final String code;
diff --git a/comwell_key_app/lib/services/models/simple_user_dto.dart b/comwell_key_app/lib/services/models/simple_user_dto.dart
new file mode 100644
index 00000000..283fdedc
--- /dev/null
+++ b/comwell_key_app/lib/services/models/simple_user_dto.dart
@@ -0,0 +1,43 @@
+class SimpleUserDto {
+ final String? firstName;
+ final String? lastName;
+ final String? phoneNumber;
+ final String? birthDate;
+ final String? addressStreet;
+ final String? addressZip;
+ final String? addressCity;
+ final String? addressCountry;
+ final String? gender;
+ final String? locale;
+ final Map<String, dynamic>? permissions;
+
+ SimpleUserDto({
+ this.firstName,
+ this.lastName,
+ this.phoneNumber,
+ this.birthDate,
+ this.addressStreet,
+ this.addressZip,
+ this.addressCity,
+ this.addressCountry,
+ this.gender,
+ this.locale,
+ this.permissions,
+ });
+
+ Map<String, dynamic> toJson() {
+ final map = <String, dynamic>{};
+ if (firstName != null) map['firstName'] = firstName;
+ if (lastName != null) map['lastName'] = lastName;
+ if (phoneNumber != null) map['phoneNumber'] = phoneNumber;
+ if (birthDate != null) map['birthDate'] = birthDate;
+ if (addressStreet != null) map['addressStreet'] = addressStreet;
+ if (addressZip != null) map['addressZip'] = addressZip;
+ if (addressCity != null) map['addressCity'] = addressCity;
+ if (addressCountry != null) map['addressCountry'] = addressCountry;
+ if (gender != null) map['gender'] = gender;
+ if (locale != null) map['locale'] = locale;
+ if (permissions != null) map['permissions'] = permissions;
+ return map;
+ }
+}
diff --git a/comwell_key_app/lib/services/models/user_dto.dart b/comwell_key_app/lib/services/models/user_dto.dart
index 5dca6e49..e6e9f063 100644
--- a/comwell_key_app/lib/services/models/user_dto.dart
+++ b/comwell_key_app/lib/services/models/user_dto.dart
@@ -1,4 +1,4 @@
-import 'package:comwell_key_app/profile_settings/model/user.dart';
+import 'package:comwell_key_app/services/models/user_permissions.dart';
import 'package:json_annotation/json_annotation.dart';
part '../../.generated/services/models/user_dto.g.dart';
@@ -6,64 +6,65 @@ part '../../.generated/services/models/user_dto.g.dart';
@JsonSerializable()
class UserDto {
final int id;
+ final String userId;
+ final String? hmsId;
final String firstName;
final String lastName;
final String email;
- final String? clubId;
- final String? clubLevel;
- final String? clubLevelName;
+ final bool emailVerified;
+ final bool isClubMember;
final DateTime? birthDate;
- final String? phoneNumber;
+ final DateTime? createDate;
+ final String? companyId;
+ final String? companyName;
+ final String? symplifyId;
+ final String phoneNumber;
final String? gender;
- final String? addressStreet;
- final String? addressZip;
- final String? addressCity;
- final String? addressCountry;
- final int? points;
- final String? locale;
+ final String addressStreet;
+ final String addressZip;
+ final String addressCity;
+ final String addressCountry;
+ final int points;
+ final DateTime? signUpDate;
+ final String? signUpCampaign;
+ final String? signUpSource;
+ final String locale;
+ final bool wasRecentlyCreated;
+ final UserPermissions? permissions;
+ final String? nationality;
UserDto({
required this.id,
+ this.userId = '',
+ this.hmsId,
required this.firstName,
required this.lastName,
required this.email,
- required this.clubId,
- required this.clubLevel,
- required this.clubLevelName,
- required this.birthDate,
- required this.phoneNumber,
- required this.gender,
- required this.addressStreet,
- required this.addressZip,
- required this.addressCity,
- required this.addressCountry,
+ required this.emailVerified,
+ required this.isClubMember,
+ this.birthDate,
+ this.createDate,
+ this.companyId,
+ this.companyName,
+ this.symplifyId,
+ this.phoneNumber = '',
+ this.gender,
+ this.addressStreet = '',
+ this.addressZip = '',
+ this.addressCity = '',
+ this.addressCountry = '',
required this.points,
- required this.locale,
+ this.signUpDate,
+ this.signUpCampaign,
+ this.signUpSource,
+ this.locale = '',
+ required this.wasRecentlyCreated,
+ this.permissions,
+ this.nationality,
});
factory UserDto.fromJson(Map<String, dynamic> json) =>
_$UserDtoFromJson(json);
Map<String, dynamic> toJson() => _$UserDtoToJson(this);
-
- UserDto toUserDto(User user) {
- return UserDto(
- id: user.id,
- firstName: user.firstName,
- lastName: user.lastName,
- email: user.email,
- clubId: user.shopperReference,
- clubLevel: user.clubLevel ?? '',
- clubLevelName: user.clubLevelName ?? '',
- birthDate: user.birthDate,
- phoneNumber: user.phoneNumber,
- gender: user.gender ?? '',
- addressStreet: user.address.street,
- addressZip: user.address.zipCode,
- addressCity: user.address.city,
- addressCountry: user.addressCountry,
- points: user.points,
- locale: 'en',
- );
- }
}
diff --git a/comwell_key_app/lib/services/models/user_permissions.dart b/comwell_key_app/lib/services/models/user_permissions.dart
new file mode 100644
index 00000000..2eb8a2c7
--- /dev/null
+++ b/comwell_key_app/lib/services/models/user_permissions.dart
@@ -0,0 +1,31 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part '../../.generated/services/models/user_permissions.g.dart';
+
+@JsonSerializable()
+class UserPermissions {
+ @JsonKey(name: 'GuestOptIn1')
+ final bool guestOptIn1;
+
+ @JsonKey(name: 'GuestOptIn2')
+ final bool guestOptIn2;
+
+ @JsonKey(name: 'GuestOptIn3')
+ final bool guestOptIn3;
+
+ @JsonKey(name: 'GuestOptIn4')
+ final bool guestOptIn4;
+
+ UserPermissions({
+ required this.guestOptIn1,
+ required this.guestOptIn2,
+ required this.guestOptIn3,
+ required this.guestOptIn4,
+ });
+
+ factory UserPermissions.fromJson(Map<String, dynamic> json) =>
+ _$UserPermissionsFromJson(json);
+
+ Map<String, dynamic> toJson() => _$UserPermissionsToJson(this);
+}
+
diff --git a/comwell_key_app/lib/services/utils/api_endpoints.dart b/comwell_key_app/lib/services/utils/api_endpoints.dart
index d7e6e1a9..8eecbd55 100644
--- a/comwell_key_app/lib/services/utils/api_endpoints.dart
+++ b/comwell_key_app/lib/services/utils/api_endpoints.dart
@@ -6,11 +6,12 @@ class ApiEndpoints {
static const String getCurrentBookings = '/booking/v1/GetCurrentBookings';
static const String getPastBookings = '/booking/v1/GetPastBookings';
static const String getCancelledBookings = '/booking/v1/GetCancelledBookings';
- static const String preRegistration = '/booking/v1/Preregistration';
+ static const String preRegistration = '/booking/v1/PreArrivalByToken';
static const String checkIn = '/booking/v1/CheckIn';
static const String checkOut = '/booking/v1/CheckOut';
static const String getBookingDetails =
'/booking/v1/GetBookingDetails?confirmationId={confirmationId}&hotelCode={hotelCode}';
+ static const String findBookingByConfirmationId = '/booking/v1/FindBooking';
//Up sales endpoints
static const String upSales = '/booking/v1/GetAvailableUpsells';
@@ -39,7 +40,7 @@ class ApiEndpoints {
static const String deleteGuest = '/Members/v1/DeleteGuest';
static const String clubSignup = '/Members/v1/ClubSignup';
static const String updateGuestData = '/Members/v1/UpdateGuestData';
- static const String communicationPreference = '/Members/v1/guests';
+ static const String communicationPreference = '/Members/v1/GetPermissions';
// Key endpoints
static const String createEndpointRegistration =
@@ -48,4 +49,14 @@ class ApiEndpoints {
// Hotel endpoints
static const String getHotelInfo = '/Content/v1/api/v1/hotel';
+
+ // Room sharing endpoints
+ static const String createRoomSharingLink = '/booking/v1/CreateRoomSharing';
+ static const String consumeRoomSharingLink =
+ '/booking/v1/ConsumeRoomSharing?sharingId={sharingId}&hotelCode={hotelCode}';
+
+ static const String removeGuestsFromBooking = '/booking/v1/RemovePersonFromSharedReservation';
+
+ // Push notifications endpoints
+ static const String registerPushToken = '{url}/rest/{customerid}/apps/{appid}/devices';
}
diff --git a/comwell_key_app/lib/share/cubit/share_booking_cubit.dart b/comwell_key_app/lib/share/cubit/share_booking_cubit.dart
index 46f76b5d..3e682e0d 100644
--- a/comwell_key_app/lib/share/cubit/share_booking_cubit.dart
+++ b/comwell_key_app/lib/share/cubit/share_booking_cubit.dart
@@ -1,4 +1,7 @@
import 'package:bloc/bloc.dart';
+import 'package:comwell_key_app/overview/models/guest.dart';
+import 'package:comwell_key_app/share/share_booking_repository.dart';
+import 'package:comwell_key_app/utils/locator.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
@@ -7,12 +10,42 @@ import 'package:share_plus/share_plus.dart';
part 'share_booking_state.dart';
class ShareBookingCubit extends Cubit<ShareBookingState> {
+ final ShareBookingRepository shareBookingRepository =
+ locator<ShareBookingRepository>();
+
ShareBookingCubit() : super(const ShareBookingState.initial());
- void updateSelectedGuests(Iterable<String> guests) {
+ void updateSelectedGuests(Iterable<Guest> guests) {
emit(state.updateSelectedGuests(guests));
}
+ void removeGuests(List<int> guestIds) {
+ final newSelectedGuests = state.selectedGuests.toList();
+ newSelectedGuests.removeWhere((guest) => guestIds.contains(guest.id));
+ emit(state.updateSelectedGuests(newSelectedGuests));
+ }
+
+ Future<void> createRoomSharingLink(Booking booking) async {
+ emit(state.loading());
+
+ final link = await shareBookingRepository.createRoomSharingLink(
+ booking.confirmationNumber, booking.hotelCode, 0);
+
+ if (link != null && link.isNotEmpty) {
+ Share.share(
+ 'share_booking_message'.tr(args: [
+ "${booking.firstName} ${booking.lastName}",
+ booking.hotelName,
+ link
+ ]),
+ subject: 'comwell_booking'.tr(),
+ );
+ emit(state.loaded());
+ } else {
+ emit(state.setupError(StateError('share_link_creation_failed')));
+ }
+ }
+
Future<void> shareBooking(Booking booking) async {
// TODO: Implement actual sharing logic here
Share.share(
diff --git a/comwell_key_app/lib/share/cubit/share_booking_state.dart b/comwell_key_app/lib/share/cubit/share_booking_state.dart
index e2282d84..504cbb5c 100644
--- a/comwell_key_app/lib/share/cubit/share_booking_state.dart
+++ b/comwell_key_app/lib/share/cubit/share_booking_state.dart
@@ -1,7 +1,7 @@
part of 'share_booking_cubit.dart';
class ShareBookingState extends Equatable {
- final Iterable<String> selectedGuests;
+ final Iterable<Guest> selectedGuests;
final bool isLoading;
final Error? error;
@@ -15,14 +15,15 @@ class ShareBookingState extends Equatable {
ShareBookingState loading() => _copyWith(isLoading: true);
- ShareBookingState setupError({required Error error}) => _copyWith(error: error, isLoading: false);
+ ShareBookingState setupError([Error? newError]) =>
+ _copyWith(error: newError ?? StateError('share_booking_error'), isLoading: false);
ShareBookingState loaded() => _copyWith(isLoading: false);
- ShareBookingState updateSelectedGuests(Iterable<String> guests) =>
+ ShareBookingState updateSelectedGuests(Iterable<Guest> guests) =>
_copyWith(selectedGuests: guests);
- ShareBookingState _copyWith({Iterable<String>? selectedGuests, bool? isLoading, Error? error}) {
+ ShareBookingState _copyWith({Iterable<Guest>? selectedGuests, bool? isLoading, Error? error}) {
return ShareBookingState._(
selectedGuests: selectedGuests ?? this.selectedGuests,
isLoading: isLoading ?? this.isLoading,
diff --git a/comwell_key_app/lib/share/share_booking_page.dart b/comwell_key_app/lib/share/share_booking_page.dart
index 240b3089..c8e30336 100644
--- a/comwell_key_app/lib/share/share_booking_page.dart
+++ b/comwell_key_app/lib/share/share_booking_page.dart
@@ -1,140 +1,53 @@
+import 'dart:async';
import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:easy_localization/easy_localization.dart';
+import 'package:comwell_key_app/utils/templates/share_booking_base_template.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:comwell_key_app/share/cubit/share_booking_cubit.dart';
-class ShareBookingPage extends StatelessWidget {
+class ShareBookingPage extends StatefulWidget {
final Booking booking;
const ShareBookingPage({super.key, required this.booking});
+ @override
+ State<ShareBookingPage> createState() => _ShareBookingPageState();
+}
+
+class _ShareBookingPageState extends State<ShareBookingPage> {
+ Timer? _errorTimer;
+
+ @override
+ void dispose() {
+ _errorTimer?.cancel();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final cubit = context.read<ShareBookingCubit>();
- return Scaffold(
- backgroundColor: sandColor,
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- const SizedBox(width: 52),
- Text('share_booking_page_title'.tr(),
- style: theme.textTheme.titleMedium?.copyWith(
- color: Colors.white,
- fontWeight: FontWeight.bold,
- )),
- Padding(
- padding: const EdgeInsets.only(right: 16),
- child: ElevatedButton(
- onPressed: () {
- Navigator.of(context).pop();
- },
- style: ElevatedButton.styleFrom(
- minimumSize: const Size(36, 36),
- padding: EdgeInsets.zero,
- backgroundColor: Theme.of(context).colorScheme.surface,
- shape: const CircleBorder(),
- elevation: 0,
- ),
- child: Icon(
- Icons.close,
- color: Theme.of(context).colorScheme.onSurface,
- size: 24,
- ),
- ),
- )
- ],
- ),
- Card(
- clipBehavior: Clip.antiAlias,
- child: Stack(
- children: [
- Image.asset(
- 'assets/images/booking_background.png',
- height: 450,
- width: 330,
- fit: BoxFit.cover,
- ),
- Positioned(
- top: 16,
- left: 16,
- child: Text(
- booking.hotelName,
- style: theme.textTheme.headlineLarge?.copyWith(
- color: Theme.of(context).colorScheme.surface,
- ),
- ),
- ),
- Positioned(
- bottom: 16,
- left: 16,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- Text(DateFormat('d. MMM').format(booking.startDate),
- style: theme.textTheme.bodyMedium?.copyWith(
- color: Theme.of(context).colorScheme.surface,
- )),
- Icon(Icons.arrow_forward,
- size: 16, color: Theme.of(context).colorScheme.surface),
- Text(DateFormat('d. MMM').format(booking.endDate),
- style: theme.textTheme.bodyMedium?.copyWith(
- color: Theme.of(context).colorScheme.surface,
- )),
- ],
- ),
- Text(
- '${booking.adults} ${booking.adults > 1 ? 'adults'.tr() : 'adult'.tr()}${booking.children > 0 ? ' | ${booking.children} ${booking.children > 1 ? 'children'.tr() : 'child'.tr()}' : ''}',
- style: theme.textTheme.bodySmall?.copyWith(
- color: Theme.of(context).colorScheme.surface.withValues(alpha: 0.7),
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 40),
- child: Text(
- 'share_booking_page_subtitle'.tr(),
- style: Theme.of(context).textTheme.bodySmall?.copyWith(
- color: Theme.of(context).colorScheme.surface.withValues(alpha: 0.8),
- ),
- textAlign: TextAlign.center,
- ),
- ),
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: SizedBox(
- width: double.infinity,
- child: ElevatedButton(
- onPressed: () {
- cubit.shareBooking(booking);
- },
- style: ElevatedButton.styleFrom(
- backgroundColor: Theme.of(context).colorScheme.surface,
- minimumSize: const Size.fromHeight(52),
- elevation: 0,
- ),
- child: Text('share_booking_page_share_button'.tr(),
- style: theme.textTheme.bodyMedium?.copyWith(
- color: Theme.of(context).colorScheme.onSurface,
- )),
- ),
- ),
- ),
- ],
- ),
- ),
- );
+ return BlocListener<ShareBookingCubit, ShareBookingState>(
+ listener: (context, state) {
+ if (state.error != null) {
+ _errorTimer?.cancel();
+ _errorTimer = Timer(const Duration(seconds: 5), () {
+ if (mounted) {
+ context.read<ShareBookingCubit>().clearSelection();
+ }
+ });
+ } else {
+ _errorTimer?.cancel();
+ }
+ }, child: BlocBuilder<ShareBookingCubit, ShareBookingState>(
+ builder: (context, state) {
+ final cubit = context.read<ShareBookingCubit>();
+
+ return ShareBookingBaseTemplate(
+ booking: widget.booking,
+ onClicked: () {
+ cubit.createRoomSharingLink(widget.booking);
+ },
+ isShared: false,
+ isLoading: state.isLoading,
+ error: state.error);
+ }));
}
}
diff --git a/comwell_key_app/lib/share/share_booking_repository.dart b/comwell_key_app/lib/share/share_booking_repository.dart
new file mode 100644
index 00000000..d52d6ce6
--- /dev/null
+++ b/comwell_key_app/lib/share/share_booking_repository.dart
@@ -0,0 +1,41 @@
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/services/api.dart';
+import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
+import 'package:flutter/material.dart';
+
+class ShareBookingRepository {
+ final Api api = Api();
+
+ ShareBookingRepository();
+
+ Future<String?> createRoomSharingLink(
+ String hmsConfirmationNumber, String hotelCode, int sharingType) async {
+ try {
+ final link = await api.createRoomSharingLink(
+ hmsConfirmationNumber, hotelCode, sharingType);
+ return link;
+ } catch (e) {
+ debugPrint("Error creating room sharing link: $e");
+ return null;
+ }
+ }
+
+ Future<Booking> consumeRoomSharingLink(
+ String sharingId, String hotelCode) async {
+ try {
+ final response = await api.consumeRoomSharingLink(sharingId, hotelCode);
+ return response.toBooking();
+ } catch (e) {
+ throw Exception(e);
+ }
+ }
+
+ Future<void> removeGuestsFromBooking(
+ String hmsConfirmationNumber, String hotelCode, int guestId) async {
+ try {
+ await api.removeGuestsFromBooking(hmsConfirmationNumber, hotelCode, guestId);
+ } catch (e) {
+ throw Exception(e);
+ }
+ }
+}
diff --git a/comwell_key_app/lib/up_sales/components/catalog/service_catalog.dart b/comwell_key_app/lib/up_sales/components/catalog/service_catalog.dart
index 75f5e70a..c0f82bce 100644
--- a/comwell_key_app/lib/up_sales/components/catalog/service_catalog.dart
+++ b/comwell_key_app/lib/up_sales/components/catalog/service_catalog.dart
@@ -50,7 +50,7 @@ class ServiceCatalog extends StatelessWidget {
isSinglePurchase: isSinglePurchase));
if (bookingDetailsBloc != null && isSinglePurchase) {
- bookingDetailsBloc!.add(InitialEvent());
+ bookingDetailsBloc!.add(const InitialEvent());
}
if (result is List && result.length == 2) {
diff --git a/comwell_key_app/lib/up_sales/cubit/up_sales_cubit.dart b/comwell_key_app/lib/up_sales/cubit/up_sales_cubit.dart
index 827b80a3..a4bc691d 100644
--- a/comwell_key_app/lib/up_sales/cubit/up_sales_cubit.dart
+++ b/comwell_key_app/lib/up_sales/cubit/up_sales_cubit.dart
@@ -20,7 +20,7 @@ class UpSalesCubit extends Cubit<UpSalesState> {
void init() async {
emit(state.loading());
upSales = await upSaleRepository.getRemoteUpSales(
- booking.confirmationId, booking.hotelCode);
+ booking.confirmationNumber, booking.hotelCode);
emit(UpSalesState(
selected: false,
@@ -96,7 +96,7 @@ class UpSalesCubit extends Cubit<UpSalesState> {
.toList();
await upSaleRepository.addUpSalesToBooking(
- booking.confirmationId, booking.hotelCode, roomType, addonList);
+ booking.confirmationNumber, booking.hotelCode, roomType, addonList);
await Future<void>.delayed(const Duration(seconds: 2));
emit(state.processingStateUpdated(UpSalesProcessingStateSuccess()));
} catch (e) {
diff --git a/comwell_key_app/lib/up_sales/up_sales_repository.dart b/comwell_key_app/lib/up_sales/up_sales_repository.dart
index 4debc3d6..600efef6 100644
--- a/comwell_key_app/lib/up_sales/up_sales_repository.dart
+++ b/comwell_key_app/lib/up_sales/up_sales_repository.dart
@@ -48,7 +48,7 @@ class UpSalesRepository {
.upsalesDAO
.getUpsaleByConfirmationNumber(confirmationId);
return upsales;
- } on Exception catch (e) {
+ } catch (e) {
debugPrint("Error checking if up sales exists: $e");
return null;
}
diff --git a/comwell_key_app/lib/utils/firebase.dart b/comwell_key_app/lib/utils/firebase.dart
index bbab8fd7..a6a6ce50 100644
--- a/comwell_key_app/lib/utils/firebase.dart
+++ b/comwell_key_app/lib/utils/firebase.dart
@@ -1,11 +1,18 @@
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../firebase_options_prod.dart' as prod;
import '../firebase_options_stage.dart' as stage;
import '../firebase_options_dev.dart' as dev;
+@pragma('vm:entry-point')
+Future<void> _backgroundHandler(RemoteMessage message) async {
+ debugPrint('Background message: ${message.data}');
+}
+
Future<void> configureFirebase() async {
final firebaseOptions = switch (appFlavor?.toLowerCase()) {
'prod' => prod.DefaultFirebaseOptions.currentPlatform,
@@ -22,4 +29,5 @@ Future<void> configureFirebase() async {
adUserDataConsentGranted: true,
adPersonalizationSignalsConsentGranted: true,
);
+ FirebaseMessaging.onBackgroundMessage(_backgroundHandler);
}
diff --git a/comwell_key_app/lib/utils/locator.dart b/comwell_key_app/lib/utils/locator.dart
index ea826842..cfaf30a5 100644
--- a/comwell_key_app/lib/utils/locator.dart
+++ b/comwell_key_app/lib/utils/locator.dart
@@ -3,6 +3,7 @@ import 'package:comwell_key_app/check_out/check_out_repository.dart';
import 'package:comwell_key_app/choose_share_room/choose_share_room_repository.dart';
import 'package:comwell_key_app/contact/repository/contact_repository.dart';
import 'package:comwell_key_app/database/comwell_db.dart';
+import 'package:comwell_key_app/find_booking/find_booking_repository.dart';
import 'package:comwell_key_app/hotel_information/repository/hotel_information_repository.dart';
import 'package:comwell_key_app/housekeeping/housekeeping_repository.dart';
import 'package:comwell_key_app/key/repository/key_repository.dart';
@@ -12,6 +13,8 @@ import 'package:comwell_key_app/overview/repository/overview_repository.dart';
import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
+import 'package:comwell_key_app/share/share_booking_repository.dart';
+import 'package:comwell_key_app/push_notifications/push_notification_repository.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/up_sales/up_sales_repository.dart';
import 'package:comwell_key_app/utils/secure_storage.dart';
@@ -67,10 +70,13 @@ void setupLocator() {
locator.registerFactory<UpSalesRepository>(() => UpSalesRepository());
locator.registerFactory<ChooseShareRoomRepository>(
() => ChooseShareRoomRepository());
+ locator.registerFactory<FindBookingRepository>(() => FindBookingRepository());
locator.registerSingleton<GlobalKey<NavigatorState>>(rootNavigatorKey);
locator.registerFactory<CheckOutRepository>(() => CheckOutRepository());
locator.registerFactory<MyBookingRepository>(() => MyBookingRepository());
+ locator.registerFactory<ShareBookingRepository>(() => ShareBookingRepository());
locator.registerSingleton<SecureStorage>(SecureStorage());
locator.registerFactory<HouseKeepingRepository>(() => HouseKeepingRepository());
+ locator.registerFactory<PushNotificationRepository>(() => PushNotificationRepository());
}
}
diff --git a/comwell_key_app/lib/utils/seos_repository.dart b/comwell_key_app/lib/utils/seos_repository.dart
index ea66e8fb..4ab55cce 100644
--- a/comwell_key_app/lib/utils/seos_repository.dart
+++ b/comwell_key_app/lib/utils/seos_repository.dart
@@ -1,6 +1,7 @@
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/secure_storage.dart';
+import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
@@ -45,6 +46,8 @@ class SeosRepository {
} else {
final invitationCode = await api.createEndpointRegistration();
await seosMobileKeysPlugin.setupEndpoint(invitationCode);
+
+ await Future.delayed(const Duration(seconds: 2));
await seosMobileKeysPlugin.updateEndpoint();
}
} on PlatformException catch (e) {
@@ -63,10 +66,10 @@ class SeosRepository {
}
}
- Future<void> provisionKey({required String bookingId}) async {
+ Future<void> provisionKey({required String bookingId, required String hotelCode}) async {
if (!_pluginStarted) return;
try {
- await api.provisionKey(bookingId);
+ await api.provisionKey(bookingId, hotelCode);
await seosMobileKeysPlugin.updateEndpoint();
} catch (e) {
throw Exception('Failed to provision a key - ${e.toString()}');
@@ -84,4 +87,13 @@ class SeosRepository {
throw Exception('Failed to list keys - ${e.toString()}');
}
}
+
+ Future<void> terminateEndpoint() async {
+ if (!_pluginStarted) return;
+ try {
+ await seosMobileKeysPlugin.terminateEndpoint();
+ } catch (e) {
+ throw Exception('Failed to terminate endpoint - ${e.toString()}');
+ }
+ }
}
diff --git a/comwell_key_app/lib/utils/share_button_utils.dart b/comwell_key_app/lib/utils/share_button_utils.dart
index 0e7c3bfc..56a31ce4 100644
--- a/comwell_key_app/lib/utils/share_button_utils.dart
+++ b/comwell_key_app/lib/utils/share_button_utils.dart
@@ -2,7 +2,7 @@ import 'package:comwell_key_app/overview/models/guest.dart';
Iterable<String> generateInitials(Iterable<Guest> guests) {
return guests
- .map((guest) => guest.name.split(' ').map((name) => name[0].toUpperCase()).join(''));
+ .map((guest) => "${guest.firstName.isNotEmpty ? guest.firstName[0].toUpperCase() : ''}${guest.lastName.isNotEmpty ? guest.lastName[0].toUpperCase() : ''}");
}
String generateInitialsString(String name) {
diff --git a/comwell_key_app/lib/utils/templates/share_booking_base_template.dart b/comwell_key_app/lib/utils/templates/share_booking_base_template.dart
new file mode 100644
index 00000000..bbe338ab
--- /dev/null
+++ b/comwell_key_app/lib/utils/templates/share_booking_base_template.dart
@@ -0,0 +1,197 @@
+import 'package:comwell_key_app/common/components/comwell_error_widget.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class ShareBookingBaseTemplate extends StatelessWidget {
+ final Booking booking;
+ final bool isShared;
+ final void Function() onClicked;
+ final bool isLoading;
+ final Error? error;
+ const ShareBookingBaseTemplate(
+ {super.key,
+ required this.booking,
+ required this.onClicked,
+ required this.isShared,
+ required this.isLoading,
+ this.error});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Scaffold(
+ backgroundColor: sandColor,
+ body: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ if (error != null)
+ TweenAnimationBuilder<double>(
+ tween: Tween<double>(begin: -1.0, end: 0.0),
+ duration: const Duration(milliseconds: 400),
+ curve: Curves.easeOutCubic,
+ builder: (context, value, child) {
+ return Transform.translate(
+ offset: Offset(0, value * 200),
+ child: Opacity(
+ opacity: 1.0 + value,
+ child: child,
+ ),
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.only(top: 40.0),
+ child: error != null
+ ? ComwellErrorWidget(
+ title: "share_booking_page_error_title".tr(),
+ subtitle: "share_booking_page_error_subtitle".tr(),
+ border: true,
+ small: true,
+ )
+ : const SizedBox.shrink(),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 16.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ const SizedBox(width: 52),
+ Text(
+ isShared
+ ? 'received_shared_booking_page_title'.tr()
+ : 'share_booking_page_title'.tr(),
+ style: theme.textTheme.titleMedium?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ )),
+ Padding(
+ padding: const EdgeInsets.only(right: 16),
+ child: isShared
+ ? const SizedBox.shrink()
+ : ElevatedButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ style: ElevatedButton.styleFrom(
+ minimumSize: const Size(36, 36),
+ padding: EdgeInsets.zero,
+ backgroundColor:
+ Theme.of(context).colorScheme.surface,
+ shape: const CircleBorder(),
+ elevation: 0,
+ ),
+ child: Icon(
+ Icons.close,
+ color: Theme.of(context).colorScheme.onSurface,
+ size: 24,
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ Card(
+ clipBehavior: Clip.antiAlias,
+ child: Stack(
+ children: [
+ Image.asset(
+ 'assets/images/booking_background.png',
+ height: 450,
+ width: 330,
+ fit: BoxFit.cover,
+ ),
+ Positioned(
+ top: 16,
+ left: 16,
+ child: Text(
+ booking.hotelName,
+ style: theme.textTheme.headlineLarge?.copyWith(
+ color: Theme.of(context).colorScheme.surface,
+ ),
+ ),
+ ),
+ Positioned(
+ bottom: 16,
+ left: 16,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Text(DateFormat('d. MMM').format(booking.startDate),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Theme.of(context).colorScheme.surface,
+ )),
+ Icon(Icons.arrow_forward,
+ size: 16,
+ color: Theme.of(context).colorScheme.surface),
+ Text(DateFormat('d. MMM').format(booking.endDate),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Theme.of(context).colorScheme.surface,
+ )),
+ ],
+ ),
+ Text(
+ '${booking.guests.length} ${booking.guests.length > 1 ? 'guests'.tr() : 'guest'.tr()}',
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: Theme.of(context)
+ .colorScheme
+ .surface
+ .withValues(alpha: 0.7),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 40),
+ child: Text(
+ isShared
+ ? 'received_shared_booking_page_subtitle'.tr(args: [
+ "${booking.firstName} ${booking.lastName}",
+ ])
+ : 'share_booking_page_subtitle'.tr(),
+ style: Theme.of(context).textTheme.bodySmall?.copyWith(
+ color: Theme.of(context)
+ .colorScheme
+ .surface
+ .withValues(alpha: 0.8),
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: onClicked,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ minimumSize: const Size.fromHeight(52),
+ elevation: 0,
+ ),
+ child: isLoading
+ ? const CircularProgressIndicator(
+ color: sandColor,
+ backgroundColor: colorTertiary,
+ strokeWidth: 2,
+ )
+ : Text(isShared ? 'generic_ok'.tr() : 'share_booking_page_share_button'.tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Theme.of(context).colorScheme.onSurface,
+ )),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/pubspec.yaml b/comwell_key_app/pubspec.yaml
index bc2316c6..ad19ce20 100644
--- a/comwell_key_app/pubspec.yaml
+++ b/comwell_key_app/pubspec.yaml
@@ -2,8 +2,7 @@ name: comwell_key_app
description: This app needs a description
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-version: 0.0.2+15
-
+version: 0.0.2+19
environment:
sdk: '>=3.5.0 <4.0.0'
dependencies:
@@ -15,16 +14,16 @@ dependencies:
focus_detector: ^2.0.0
permission_handler: ^11.3.0
go_router: ^14.7.2
- bloc: ^8.1.4
- flutter_bloc: ^8.1.6
+ bloc: ^9.1.0
+ flutter_bloc: ^9.1.1
easy_localization: ^3.0.7
dotenv: ^4.2.0
flutter_dotenv: ^5.1.0
equatable: ^2.0.5
flutter_secure_storage: ^9.2.2
lottie: ^3.1.2
- bloc_test: ^9.1.7
- mockito: ^5.4.4
+ bloc_test: ^10.0.0
+ #mockito: ^5.5.1
fake_async: ^1.3.1
get_it: ^7.7.0
flutter_native_splash: ^2.4.6
@@ -40,15 +39,15 @@ dependencies:
flutter_launcher_icons: ^0.14.1
slider_button: ^2.1.0
country_code_picker: ^3.1.0
- adyen_checkout: ^1.5.0
+ adyen_checkout: ^1.7.0
json_annotation: ^4.9.0
firebase_core: ^3.14.0
firebase_analytics: ^11.5.0
+ firebase_messaging: ^15.2.0
url_launcher: ^6.3.1
drift: ^2.27.0
sqlcipher_flutter_libs: ^0.6.4
uuid: ^4.5.1
- sqlite3: ^2.6.0
path: ^1.9.0
path_provider: ^2.1.5
package_info_plus: ^8.3.0
diff --git a/comwell_key_app/scripts/run_dev.sh b/comwell_key_app/scripts/run_dev.sh
new file mode 100644
index 00000000..c272f161
--- /dev/null
+++ b/comwell_key_app/scripts/run_dev.sh
@@ -0,0 +1 @@
+fvm flutter run --flavor Develop
\ No newline at end of file
diff --git a/comwell_key_app/test/authentication_test/authentication_repository.dart b/comwell_key_app/test/authentication_test/authentication_repository.dart
index 4e34440a..078affb5 100644
--- a/comwell_key_app/test/authentication_test/authentication_repository.dart
+++ b/comwell_key_app/test/authentication_test/authentication_repository.dart
@@ -1,51 +1,53 @@
-// Import necessary testing packages and mockito
+// Import necessary testing packages and mocktail
import 'package:comwell_key_app/authentication/authentication_repository.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
+import 'package:mocktail/mocktail.dart';
import 'package:seos_mobile_keys_plugin/app_usage_api.dart';
-import 'package:seos_mobile_keys_plugin/seos_mobile_keys_plugin.dart';
+import 'package:comwell_key_app/utils/seos_repository.dart';
-class MockSeosMobileKeysPlugin extends Mock implements SeosMobileKeysPlugin {}
+class MockSeosRepository extends Mock implements SeosRepository {}
class MockAuthenticationRepository extends Mock implements AuthenticationRepository {}
void main() {
- // Declare the mock SeosMobileKeysPlugin
- late SeosMobileKeysPlugin mockSeosMobileKeysPlugin;
+ // Declare the mocks
+ late MockSeosRepository mockSeosRepository;
late MockAuthenticationRepository authenticationRepository;
// Setup function runs before every test
setUp(() {
- // Initialize the mock SeosMobileKeysPlugin
+ // Initialize the mocks
TestWidgetsFlutterBinding.ensureInitialized();
- mockSeosMobileKeysPlugin = MockSeosMobileKeysPlugin();
+ mockSeosRepository = MockSeosRepository();
authenticationRepository = MockAuthenticationRepository();
-
+
+ // Stub the seos getter to return the mock SeosRepository
+ when(() => authenticationRepository.seos).thenReturn(mockSeosRepository);
});
- group('HomeRepository', () {
+ group('AuthenticationRepository - SeosRepository integration', () {
test('isEndpointSetup returns true when endpoint is setup', () async {
- when(mockSeosMobileKeysPlugin.isEndpointSetup()).thenAnswer((_) async => true);
+ when(() => mockSeosRepository.isEndpointSetup()).thenAnswer((_) async => true);
expect(await authenticationRepository.seos.isEndpointSetup(), isTrue);
});
test('isEndpointSetup returns false when endpoint is not setup', () async {
- when(mockSeosMobileKeysPlugin.isEndpointSetup()).thenAnswer((_) async => false);
-
+ when(() => mockSeosRepository.isEndpointSetup()).thenAnswer((_) async => false);
expect(await authenticationRepository.seos.isEndpointSetup(), isFalse);
});
test('refreshKeys returns a list of MobileKeysKey on success', () async {
- when(mockSeosMobileKeysPlugin.listMobileKeys()).thenAnswer((_) async => [MobileKeysKey(active: true, credentialType: 1)]);
+ when(() => mockSeosRepository.refreshKeys()).thenAnswer((_) async => [MobileKeysKey(active: true, credentialType: 1)]);
final keys = await authenticationRepository.seos.refreshKeys();
expect(keys, isNotNull);
- expect(keys, isA<List<MobileKeysKey?>>());
+ expect(keys, isA<List<MobileKeysKey>>());
+ expect(keys.length, equals(1));
});
test('refreshKeys throws an exception on failure', () async {
- when(mockSeosMobileKeysPlugin.listMobileKeys()).thenThrow(Exception('Failed to list keys'));
+ when(() => mockSeosRepository.refreshKeys()).thenThrow(Exception('Failed to list keys'));
expect(authenticationRepository.seos.refreshKeys(), throwsA(isA<Exception>()));
});
diff --git a/comwell_key_app/test/booking_details_test/booking_details_bloc_test.dart b/comwell_key_app/test/booking_details_test/booking_details_bloc_test.dart
index d02a8ea2..2e7bc644 100644
--- a/comwell_key_app/test/booking_details_test/booking_details_bloc_test.dart
+++ b/comwell_key_app/test/booking_details_test/booking_details_bloc_test.dart
@@ -1,4 +1,4 @@
-// Import necessary testing packages, bloc, and mockito
+// Import necessary testing packages, bloc, and mocktail
import 'package:comwell_key_app/booking_details/bloc/booking_details_bloc.dart';
import 'package:comwell_key_app/booking_details/booking_details_repository.dart';
import 'package:comwell_key_app/check_in/check_in_repository.dart';
@@ -9,7 +9,7 @@ import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/seos_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:bloc_test/bloc_test.dart';
-import 'package:mockito/mockito.dart';
+import 'package:mocktail/mocktail.dart';
class MockBookingDetailsRepository extends Mock
implements BookingDetailsRepository {}
@@ -51,7 +51,7 @@ void main() {
houseKeepingRepository: mockHouseKeepingRepository,
);
},
- act: (bloc) => bloc.add(InitialEvent()),
+ act: (bloc) => bloc.add(const InitialEvent()),
// expect: () => [isA<ValidKey>()], // Expecting ValidKey state to be emitted
);
diff --git a/comwell_key_app/test/booking_details_test/booking_details_repository_test.dart b/comwell_key_app/test/booking_details_test/booking_details_repository_test.dart
index 52eadc16..2a443c46 100644
--- a/comwell_key_app/test/booking_details_test/booking_details_repository_test.dart
+++ b/comwell_key_app/test/booking_details_test/booking_details_repository_test.dart
@@ -1,6 +1,6 @@
-// Import necessary testing packages and mockito
+// Import necessary testing packages and mocktail
import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
+import 'package:mocktail/mocktail.dart';
import 'package:seos_mobile_keys_plugin/seos_mobile_keys_plugin.dart';
class MockSeosMobileKeysPlugin extends Mock implements SeosMobileKeysPlugin {}
diff --git a/comwell_key_app/test/key_test/key_bloc_test.dart b/comwell_key_app/test/key_test/key_bloc_test.dart
index 366d1115..db911b12 100644
--- a/comwell_key_app/test/key_test/key_bloc_test.dart
+++ b/comwell_key_app/test/key_test/key_bloc_test.dart
@@ -4,7 +4,7 @@ import 'package:comwell_key_app/key/bloc/key_bloc.dart';
import 'package:comwell_key_app/key/repository/key_repository.dart';
import 'package:comwell_key_app/utils/seos_repository.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/mockito.dart';
+import 'package:mocktail/mocktail.dart';
import 'package:fake_async/fake_async.dart';
import 'package:seos_mobile_keys_plugin/app_usage_api.dart';
@@ -43,7 +43,8 @@ void main() {
blocTest<KeyBloc, KeyState>(
'emits [KeyState.searchForKeys, KeyState.validKeys, KeyState.scanning, KeyState.openClosestReaderSuccess] when SearchForKeys and StartScanning are added',
build: () {
- when(mockKeyRepository.checkDeviceInfo()).thenAnswer((_) async => {});
+ when(() => mockKeyRepository.checkDeviceInfo()).thenAnswer((_) async => {});
+ when(() => mockSeosRepository.refreshKeys()).thenAnswer((_) async => [mockMobileKeysKey]);
return keyBloc;
},
act: (bloc) {
@@ -63,7 +64,7 @@ void main() {
blocTest<KeyBloc, KeyState>(
'emits [KeyState.searchForKeys, KeyState.searchForKeysError] when SearchForKeys fails',
build: () {
- when(mockSeosRepository.refreshKeys()).thenThrow(Exception('Failed to list keys'));
+ when(() => mockSeosRepository.refreshKeys()).thenThrow(Exception('Failed to list keys'));
return keyBloc;
},
act: (bloc) => bloc.add(SearchForKeys()),
@@ -76,7 +77,7 @@ void main() {
blocTest<KeyBloc, KeyState>(
'emits [KeyState.scanningError] when StartScanning fails',
build: () {
- when(mockKeyRepository.checkDeviceInfo()).thenThrow(Exception('Device info error'));
+ when(() => mockKeyRepository.checkDeviceInfo()).thenThrow(Exception('Device info error'));
return keyBloc;
},
act: (bloc) => bloc.add(const StartScanning()),
diff --git a/comwell_key_app/test/overview_test/overview_cubic_test.dart b/comwell_key_app/test/overview_test/overview_cubic_test.dart
index 2d29c4da..70883b36 100644
--- a/comwell_key_app/test/overview_test/overview_cubic_test.dart
+++ b/comwell_key_app/test/overview_test/overview_cubic_test.dart
@@ -1,5 +1,4 @@
import 'package:bloc_test/bloc_test.dart';
-import 'package:comwell_key_app/overview/models/guest.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:comwell_key_app/overview/cubit/overview_cubit.dart';
@@ -32,28 +31,29 @@ void main() {
Booking(
id: '1',
reservationStatus: ReservationStatus.newreservation,
- confirmationId: "s",
+ confirmationNumber: "s",
roomNumber: '',
startDate: DateTime(2021, 10, 10),
endDate: DateTime(2021, 10, 10),
- hmsConfirmationNumber: "hmsConfirmationNumber",
image: '',
hotelName: '',
roomType: '',
children: 2,
adults: 2,
hotelCode: '',
- booker: const Guest(name: "name", id: "id"),
+ firstName: "John",
+ lastName: "Doe",
+ bookerFirstName: "John",
+ bookerLastName: "Doe",
+ isPrimaryGuest: true,
bookingDate: DateTime(2021, 10, 10),
digitalCard: true,
- totalCharge: 100,
balance: 100,
maskedCardNumber: "1234567890",
- rooms: const [],
addOnItems: const [])
],
- past: [],
- cancelled: [],
+ past: const [],
+ cancelled: const [],
));
return overviewCubit;
},
@@ -68,26 +68,27 @@ void main() {
reservationStatus: ReservationStatus.newreservation,
roomNumber: '',
startDate: DateTime(2021, 10, 10),
- hmsConfirmationNumber: "hmsConfirmationNumber",
endDate: DateTime(2021, 10, 10),
image: '',
hotelName: '',
- confirmationId: "",
+ confirmationNumber: "",
roomType: '',
children: 2,
adults: 2,
hotelCode: '',
- booker: const Guest(name: "name", id: "id"),
+ firstName: "John",
+ lastName: "Doe",
+ bookerFirstName: "John",
+ bookerLastName: "Doe",
+ isPrimaryGuest: true,
bookingDate: DateTime(2021, 10, 10),
digitalCard: true,
- totalCharge: 100,
balance: 100,
maskedCardNumber: "1234567890",
- rooms: const [],
addOnItems: const [])
],
- past: [],
- cancelled: [],
+ past: const [],
+ cancelled: const [],
),
),
],
@@ -124,22 +125,23 @@ void main() {
roomNumber: '',
startDate: DateTime(2021, 10, 10),
endDate: DateTime(2021, 10, 10),
- confirmationId: "",
+ confirmationNumber: "",
image: '',
hotelName: '',
roomType: "",
children: 2,
adults: 2,
hotelCode: '',
- booker: const Guest(name: "name", id: "id"),
+ firstName: "John",
+ lastName: "Doe",
+ bookerFirstName: "John",
+ bookerLastName: "Doe",
+ isPrimaryGuest: true,
bookingDate: DateTime(2021, 10, 10),
digitalCard: true,
- totalCharge: 100,
- hmsConfirmationNumber: "hmsConfirmationNumber",
- reservationStatus: ReservationStatus.newreservation,
balance: 100,
+ reservationStatus: ReservationStatus.newreservation,
maskedCardNumber: "1234567890",
- rooms: const [],
addOnItems: const [])),
expect: () => [
OverviewLoaded(
@@ -153,23 +155,24 @@ void main() {
image: '',
hotelName: '',
roomType: '',
- confirmationId: "",
+ confirmationNumber: "",
children: 2,
adults: 2,
hotelCode: '',
- booker: const Guest(name: "name", id: "id"),
+ firstName: "John",
+ lastName: "Doe",
+ bookerFirstName: "John",
+ bookerLastName: "Doe",
+ isPrimaryGuest: true,
bookingDate: DateTime(2021, 10, 10),
digitalCard: true,
- totalCharge: 100,
- hmsConfirmationNumber: "hmsConfirmationNumber",
- reservationStatus: ReservationStatus.newreservation,
balance: 100,
+ reservationStatus: ReservationStatus.newreservation,
maskedCardNumber: "1234567890",
- rooms: const [],
addOnItems: const [])
],
- past: [],
- cancelled: [],
+ past: const [],
+ cancelled: const [],
),
),
],
diff --git a/comwell_key_app/test/overview_test/overview_repository_test.dart b/comwell_key_app/test/overview_test/overview_repository_test.dart
index e07fe268..14a853f2 100644
--- a/comwell_key_app/test/overview_test/overview_repository_test.dart
+++ b/comwell_key_app/test/overview_test/overview_repository_test.dart
@@ -1,4 +1,4 @@
-import 'package:comwell_key_app/overview/models/guest.dart';
+
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
@@ -21,30 +21,31 @@ void main() {
current: [
Booking(
id: '1',
+ firstName: 'John',
+ lastName: 'Doe',
reservationStatus: ReservationStatus.newreservation,
roomNumber: '101',
startDate: DateTime.now(),
endDate: DateTime.now().add(const Duration(days: 1)),
- confirmationId: "s",
- hmsConfirmationNumber: "hmsConfirmationNumber",
+ confirmationNumber: "confirmationNumber",
image: 'image_url',
hotelName: 'Hotel Name',
roomType: 'Deluxe',
children: 2,
adults: 2,
hotelCode: 'H001',
- booker: const Guest(name: "name", id: userId),
+ bookerFirstName: "John",
+ bookerLastName: "Doe",
+ isPrimaryGuest: true,
bookingDate: DateTime.now(),
digitalCard: true,
- totalCharge: 100,
balance: 100,
maskedCardNumber: "1234567890",
- rooms: const [],
addOnItems: const []
),
],
- past: [],
- cancelled: [],
+ past: const [],
+ cancelled: const [],
);
when(() => overviewRepository.fetchAllBookingsForUser())
@@ -64,23 +65,24 @@ void main() {
id: '1',
roomNumber: '101',
startDate: DateTime.now(),
- confirmationId: "",
endDate: DateTime.now().add(const Duration(days: 1)),
image: 'image_url',
hotelName: 'Hotel Name',
roomType: 'Deluxe',
- hmsConfirmationNumber: "hmsConfirmationNumber",
+ confirmationNumber: "confirmationNumber",
children: 2,
adults: 2,
hotelCode: 'H001',
- booker: const Guest(name: lastName, id: "id"),
+ firstName: "John",
+ lastName: "Doe",
+ bookerFirstName: "John",
+ bookerLastName: "Doe",
+ isPrimaryGuest: true,
bookingDate: DateTime.now(),
digitalCard: true,
- totalCharge: 100,
- reservationStatus: ReservationStatus.newreservation,
balance: 100,
+ reservationStatus: ReservationStatus.newreservation,
maskedCardNumber: "1234567890",
- rooms: const [],
addOnItems: const []
);
diff --git a/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart b/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart
index 41b68823..88f48e08 100644
--- a/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart
+++ b/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart
@@ -38,9 +38,13 @@ void main() {
group('ProfileSettingsCubit', () {
final user = User(
id: 1,
- firstName: 'John',
- lastName: 'Doe',
+ userId: '1',
email: 'john.doe@example.com',
+ emailVerified: true,
+ locale: 'en',
+ wasRecentlyCreated: false,
+ firstName: 'John',
+ lastName: 'Doe',
phoneNumber: '1234567890',
gender: 'Male',
address: Address(
@@ -52,7 +56,7 @@ void main() {
birthDate: DateTime(1990, 1, 1),
addressCountry: 'SE',
shopperReference: '1234567890',
- points: 100,
+ points: 100, isClubMember: false,
);
blocTest<ProfileSettingsCubit, ProfileSettingsState>(
diff --git a/mobilekeys_sdk_plugin/android/build.gradle b/mobilekeys_sdk_plugin/android/build.gradle
index 6f857b05..0ecbc26c 100644
--- a/mobilekeys_sdk_plugin/android/build.gradle
+++ b/mobilekeys_sdk_plugin/android/build.gradle
@@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.1.3'
+ classpath 'com.android.tools.build:gradle:8.9.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}