6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit 02de35fa
Changed files
.../check_in/bloc/check_in_state.freezed.dart | 307 --------------------- .../check_in/bloc/check_in_state.freezed.dart | 307 +++++++++++++++++++++ .../lib/check_in/bloc/check_in_cubit.dart | 212 -------------- .../lib/check_in/bloc/check_in_state.dart | 80 ------ comwell_key_app/lib/check_in/check_in_flow.dart | 62 ----- comwell_key_app/lib/check_in/check_in_page.dart | 270 ------------------ .../lib/check_in/check_in_repository.dart | 83 ------ comwell_key_app/lib/check_in/check_in_route.dart | 42 --- .../check_in/components/check_in_bottom_sheet.dart | 63 ----- .../lib/check_in/pages/check_in_page_enum.dart | 20 -- .../lib/check_in/pages/check_in_payment_page.dart | 38 --- .../check_in/pages/check_in_processing_page.dart | 102 ------- .../domain/repositories/check_in_repository.dart | 82 ++++++ .../screens/check_in/bloc/check_in_cubit.dart | 212 ++++++++++++++ .../screens/check_in/bloc/check_in_state.dart | 80 ++++++ .../screens/check_in/check_in_flow.dart | 62 +++++ .../screens/check_in/check_in_page.dart | 270 ++++++++++++++++++ .../screens/check_in/check_in_route.dart | 42 +++ .../check_in/components/check_in_bottom_sheet.dart | 63 +++++ .../screens/check_in/pages/check_in_page_enum.dart | 20 ++ .../check_in/pages/check_in_payment_page.dart | 38 +++ .../check_in/pages/check_in_processing_page.dart | 102 +++++++ .../components/prereg_bottom_button.dart | 5 +- .../pregistration/cubit/preregistration_cubit.dart | 6 +- comwell_key_app/lib/routing/app_router.dart | 2 +- 25 files changed, 1285 insertions(+), 1285 deletions(-)
Diff
diff --git a/comwell_key_app/lib/.generated/check_in/bloc/check_in_state.freezed.dart b/comwell_key_app/lib/.generated/check_in/bloc/check_in_state.freezed.dart
deleted file mode 100644
index 844d4295..00000000
--- a/comwell_key_app/lib/.generated/check_in/bloc/check_in_state.freezed.dart
+++ /dev/null
@@ -1,307 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-// coverage:ignore-file
-// ignore_for_file: type=lint
-// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
-
-part of '../../../check_in/bloc/check_in_state.dart';
-
-// **************************************************************************
-// FreezedGenerator
-// **************************************************************************
-
-// dart format off
-T _$identity<T>(T value) => value;
-/// @nodoc
-mixin _$CheckInState {
-
- bool get isLoading; CheckInStatus get cardState; String get roomNumber;// Payment-related fields
- List<BookingAddonItem> get items; bool get isTermsAccepted; bool get applyClubPoints; int get clubPoints; bool get showTermsError; bool get isPaymentProcessingNeeded; CheckInPaymentStatus get paymentStatus;
-/// Create a copy of CheckInState
-/// with the given fields replaced by the non-null parameter values.
-@JsonKey(includeFromJson: false, includeToJson: false)
-@pragma('vm:prefer-inline')
-$CheckInStateCopyWith<CheckInState> get copyWith => _$CheckInStateCopyWithImpl<CheckInState>(this as CheckInState, _$identity);
-
-
-
-@override
-bool operator ==(Object other) {
- return identical(this, other) || (other.runtimeType == runtimeType&&other is CheckInState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.cardState, cardState) || other.cardState == cardState)&&(identical(other.roomNumber, roomNumber) || other.roomNumber == roomNumber)&&const DeepCollectionEquality().equals(other.items, items)&&(identical(other.isTermsAccepted, isTermsAccepted) || other.isTermsAccepted == isTermsAccepted)&&(identical(other.applyClubPoints, applyClubPoints) || other.applyClubPoints == applyClubPoints)&&(identical(other.clubPoints, clubPoints) || other.clubPoints == clubPoints)&&(identical(other.showTermsError, showTermsError) || other.showTermsError == showTermsError)&&(identical(other.isPaymentProcessingNeeded, isPaymentProcessingNeeded) || other.isPaymentProcessingNeeded == isPaymentProcessingNeeded)&&(identical(other.paymentStatus, paymentStatus) || other.paymentStatus == paymentStatus));
-}
-
-
-@override
-int get hashCode => Object.hash(runtimeType,isLoading,cardState,roomNumber,const DeepCollectionEquality().hash(items),isTermsAccepted,applyClubPoints,clubPoints,showTermsError,isPaymentProcessingNeeded,paymentStatus);
-
-@override
-String toString() {
- return 'CheckInState(isLoading: $isLoading, cardState: $cardState, roomNumber: $roomNumber, items: $items, isTermsAccepted: $isTermsAccepted, applyClubPoints: $applyClubPoints, clubPoints: $clubPoints, showTermsError: $showTermsError, isPaymentProcessingNeeded: $isPaymentProcessingNeeded, paymentStatus: $paymentStatus)';
-}
-
-
-}
-
-/// @nodoc
-abstract mixin class $CheckInStateCopyWith<$Res> {
- factory $CheckInStateCopyWith(CheckInState value, $Res Function(CheckInState) _then) = _$CheckInStateCopyWithImpl;
-@useResult
-$Res call({
- bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus
-});
-
-
-
-
-}
-/// @nodoc
-class _$CheckInStateCopyWithImpl<$Res>
- implements $CheckInStateCopyWith<$Res> {
- _$CheckInStateCopyWithImpl(this._self, this._then);
-
- final CheckInState _self;
- final $Res Function(CheckInState) _then;
-
-/// Create a copy of CheckInState
-/// with the given fields replaced by the non-null parameter values.
-@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? cardState = null,Object? roomNumber = null,Object? items = null,Object? isTermsAccepted = null,Object? applyClubPoints = null,Object? clubPoints = null,Object? showTermsError = null,Object? isPaymentProcessingNeeded = null,Object? paymentStatus = null,}) {
- return _then(_self.copyWith(
-isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
-as bool,cardState: null == cardState ? _self.cardState : cardState // ignore: cast_nullable_to_non_nullable
-as CheckInStatus,roomNumber: null == roomNumber ? _self.roomNumber : roomNumber // ignore: cast_nullable_to_non_nullable
-as String,items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
-as List<BookingAddonItem>,isTermsAccepted: null == isTermsAccepted ? _self.isTermsAccepted : isTermsAccepted // ignore: cast_nullable_to_non_nullable
-as bool,applyClubPoints: null == applyClubPoints ? _self.applyClubPoints : applyClubPoints // ignore: cast_nullable_to_non_nullable
-as bool,clubPoints: null == clubPoints ? _self.clubPoints : clubPoints // ignore: cast_nullable_to_non_nullable
-as int,showTermsError: null == showTermsError ? _self.showTermsError : showTermsError // ignore: cast_nullable_to_non_nullable
-as bool,isPaymentProcessingNeeded: null == isPaymentProcessingNeeded ? _self.isPaymentProcessingNeeded : isPaymentProcessingNeeded // ignore: cast_nullable_to_non_nullable
-as bool,paymentStatus: null == paymentStatus ? _self.paymentStatus : paymentStatus // ignore: cast_nullable_to_non_nullable
-as CheckInPaymentStatus,
- ));
-}
-
-}
-
-
-/// Adds pattern-matching-related methods to [CheckInState].
-extension CheckInStatePatterns on CheckInState {
-/// A variant of `map` that fallback to returning `orElse`.
-///
-/// It is equivalent to doing:
-/// ```dart
-/// switch (sealedClass) {
-/// case final Subclass value:
-/// return ...;
-/// case _:
-/// return orElse();
-/// }
-/// ```
-
-@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CheckInState value)? $default,{required TResult orElse(),}){
-final _that = this;
-switch (_that) {
-case _CheckInState() when $default != null:
-return $default(_that);case _:
- return orElse();
-
-}
-}
-/// A `switch`-like method, using callbacks.
-///
-/// Callbacks receives the raw object, upcasted.
-/// It is equivalent to doing:
-/// ```dart
-/// switch (sealedClass) {
-/// case final Subclass value:
-/// return ...;
-/// case final Subclass2 value:
-/// return ...;
-/// }
-/// ```
-
-@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CheckInState value) $default,){
-final _that = this;
-switch (_that) {
-case _CheckInState():
-return $default(_that);case _:
- throw StateError('Unexpected subclass');
-
-}
-}
-/// A variant of `map` that fallback to returning `null`.
-///
-/// It is equivalent to doing:
-/// ```dart
-/// switch (sealedClass) {
-/// case final Subclass value:
-/// return ...;
-/// case _:
-/// return null;
-/// }
-/// ```
-
-@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CheckInState value)? $default,){
-final _that = this;
-switch (_that) {
-case _CheckInState() when $default != null:
-return $default(_that);case _:
- return null;
-
-}
-}
-/// A variant of `when` that fallback to an `orElse` callback.
-///
-/// It is equivalent to doing:
-/// ```dart
-/// switch (sealedClass) {
-/// case Subclass(:final field):
-/// return ...;
-/// case _:
-/// return orElse();
-/// }
-/// ```
-
-@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus)? $default,{required TResult orElse(),}) {final _that = this;
-switch (_that) {
-case _CheckInState() when $default != null:
-return $default(_that.isLoading,_that.cardState,_that.roomNumber,_that.items,_that.isTermsAccepted,_that.applyClubPoints,_that.clubPoints,_that.showTermsError,_that.isPaymentProcessingNeeded,_that.paymentStatus);case _:
- return orElse();
-
-}
-}
-/// A `switch`-like method, using callbacks.
-///
-/// As opposed to `map`, this offers destructuring.
-/// It is equivalent to doing:
-/// ```dart
-/// switch (sealedClass) {
-/// case Subclass(:final field):
-/// return ...;
-/// case Subclass2(:final field2):
-/// return ...;
-/// }
-/// ```
-
-@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus) $default,) {final _that = this;
-switch (_that) {
-case _CheckInState():
-return $default(_that.isLoading,_that.cardState,_that.roomNumber,_that.items,_that.isTermsAccepted,_that.applyClubPoints,_that.clubPoints,_that.showTermsError,_that.isPaymentProcessingNeeded,_that.paymentStatus);case _:
- throw StateError('Unexpected subclass');
-
-}
-}
-/// A variant of `when` that fallback to returning `null`
-///
-/// It is equivalent to doing:
-/// ```dart
-/// switch (sealedClass) {
-/// case Subclass(:final field):
-/// return ...;
-/// case _:
-/// return null;
-/// }
-/// ```
-
-@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus)? $default,) {final _that = this;
-switch (_that) {
-case _CheckInState() when $default != null:
-return $default(_that.isLoading,_that.cardState,_that.roomNumber,_that.items,_that.isTermsAccepted,_that.applyClubPoints,_that.clubPoints,_that.showTermsError,_that.isPaymentProcessingNeeded,_that.paymentStatus);case _:
- return null;
-
-}
-}
-
-}
-
-/// @nodoc
-
-
-class _CheckInState extends CheckInState {
- const _CheckInState({this.isLoading = false, this.cardState = CheckInStatus.loading, this.roomNumber = "", final List<BookingAddonItem> items = const [], this.isTermsAccepted = false, this.applyClubPoints = false, this.clubPoints = 0, this.showTermsError = false, this.isPaymentProcessingNeeded = true, this.paymentStatus = CheckInPaymentStatus.idle}): _items = items,super._();
-
-
-@override@JsonKey() final bool isLoading;
-@override@JsonKey() final CheckInStatus cardState;
-@override@JsonKey() final String roomNumber;
-// Payment-related fields
- final List<BookingAddonItem> _items;
-// Payment-related fields
-@override@JsonKey() List<BookingAddonItem> get items {
- if (_items is EqualUnmodifiableListView) return _items;
- // ignore: implicit_dynamic_type
- return EqualUnmodifiableListView(_items);
-}
-
-@override@JsonKey() final bool isTermsAccepted;
-@override@JsonKey() final bool applyClubPoints;
-@override@JsonKey() final int clubPoints;
-@override@JsonKey() final bool showTermsError;
-@override@JsonKey() final bool isPaymentProcessingNeeded;
-@override@JsonKey() final CheckInPaymentStatus paymentStatus;
-
-/// Create a copy of CheckInState
-/// with the given fields replaced by the non-null parameter values.
-@override @JsonKey(includeFromJson: false, includeToJson: false)
-@pragma('vm:prefer-inline')
-_$CheckInStateCopyWith<_CheckInState> get copyWith => __$CheckInStateCopyWithImpl<_CheckInState>(this, _$identity);
-
-
-
-@override
-bool operator ==(Object other) {
- return identical(this, other) || (other.runtimeType == runtimeType&&other is _CheckInState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.cardState, cardState) || other.cardState == cardState)&&(identical(other.roomNumber, roomNumber) || other.roomNumber == roomNumber)&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.isTermsAccepted, isTermsAccepted) || other.isTermsAccepted == isTermsAccepted)&&(identical(other.applyClubPoints, applyClubPoints) || other.applyClubPoints == applyClubPoints)&&(identical(other.clubPoints, clubPoints) || other.clubPoints == clubPoints)&&(identical(other.showTermsError, showTermsError) || other.showTermsError == showTermsError)&&(identical(other.isPaymentProcessingNeeded, isPaymentProcessingNeeded) || other.isPaymentProcessingNeeded == isPaymentProcessingNeeded)&&(identical(other.paymentStatus, paymentStatus) || other.paymentStatus == paymentStatus));
-}
-
-
-@override
-int get hashCode => Object.hash(runtimeType,isLoading,cardState,roomNumber,const DeepCollectionEquality().hash(_items),isTermsAccepted,applyClubPoints,clubPoints,showTermsError,isPaymentProcessingNeeded,paymentStatus);
-
-@override
-String toString() {
- return 'CheckInState(isLoading: $isLoading, cardState: $cardState, roomNumber: $roomNumber, items: $items, isTermsAccepted: $isTermsAccepted, applyClubPoints: $applyClubPoints, clubPoints: $clubPoints, showTermsError: $showTermsError, isPaymentProcessingNeeded: $isPaymentProcessingNeeded, paymentStatus: $paymentStatus)';
-}
-
-
-}
-
-/// @nodoc
-abstract mixin class _$CheckInStateCopyWith<$Res> implements $CheckInStateCopyWith<$Res> {
- factory _$CheckInStateCopyWith(_CheckInState value, $Res Function(_CheckInState) _then) = __$CheckInStateCopyWithImpl;
-@override @useResult
-$Res call({
- bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus
-});
-
-
-
-
-}
-/// @nodoc
-class __$CheckInStateCopyWithImpl<$Res>
- implements _$CheckInStateCopyWith<$Res> {
- __$CheckInStateCopyWithImpl(this._self, this._then);
-
- final _CheckInState _self;
- final $Res Function(_CheckInState) _then;
-
-/// Create a copy of CheckInState
-/// with the given fields replaced by the non-null parameter values.
-@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? cardState = null,Object? roomNumber = null,Object? items = null,Object? isTermsAccepted = null,Object? applyClubPoints = null,Object? clubPoints = null,Object? showTermsError = null,Object? isPaymentProcessingNeeded = null,Object? paymentStatus = null,}) {
- return _then(_CheckInState(
-isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
-as bool,cardState: null == cardState ? _self.cardState : cardState // ignore: cast_nullable_to_non_nullable
-as CheckInStatus,roomNumber: null == roomNumber ? _self.roomNumber : roomNumber // ignore: cast_nullable_to_non_nullable
-as String,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
-as List<BookingAddonItem>,isTermsAccepted: null == isTermsAccepted ? _self.isTermsAccepted : isTermsAccepted // ignore: cast_nullable_to_non_nullable
-as bool,applyClubPoints: null == applyClubPoints ? _self.applyClubPoints : applyClubPoints // ignore: cast_nullable_to_non_nullable
-as bool,clubPoints: null == clubPoints ? _self.clubPoints : clubPoints // ignore: cast_nullable_to_non_nullable
-as int,showTermsError: null == showTermsError ? _self.showTermsError : showTermsError // ignore: cast_nullable_to_non_nullable
-as bool,isPaymentProcessingNeeded: null == isPaymentProcessingNeeded ? _self.isPaymentProcessingNeeded : isPaymentProcessingNeeded // ignore: cast_nullable_to_non_nullable
-as bool,paymentStatus: null == paymentStatus ? _self.paymentStatus : paymentStatus // ignore: cast_nullable_to_non_nullable
-as CheckInPaymentStatus,
- ));
-}
-
-
-}
-
-// dart format on
diff --git a/comwell_key_app/lib/.generated/presentation/screens/check_in/bloc/check_in_state.freezed.dart b/comwell_key_app/lib/.generated/presentation/screens/check_in/bloc/check_in_state.freezed.dart
new file mode 100644
index 00000000..4400324d
--- /dev/null
+++ b/comwell_key_app/lib/.generated/presentation/screens/check_in/bloc/check_in_state.freezed.dart
@@ -0,0 +1,307 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of '../../../../../presentation/screens/check_in/bloc/check_in_state.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+// dart format off
+T _$identity<T>(T value) => value;
+/// @nodoc
+mixin _$CheckInState {
+
+ bool get isLoading; CheckInStatus get cardState; String get roomNumber;// Payment-related fields
+ List<BookingAddonItem> get items; bool get isTermsAccepted; bool get applyClubPoints; int get clubPoints; bool get showTermsError; bool get isPaymentProcessingNeeded; CheckInPaymentStatus get paymentStatus;
+/// Create a copy of CheckInState
+/// with the given fields replaced by the non-null parameter values.
+@JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+$CheckInStateCopyWith<CheckInState> get copyWith => _$CheckInStateCopyWithImpl<CheckInState>(this as CheckInState, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+ return identical(this, other) || (other.runtimeType == runtimeType&&other is CheckInState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.cardState, cardState) || other.cardState == cardState)&&(identical(other.roomNumber, roomNumber) || other.roomNumber == roomNumber)&&const DeepCollectionEquality().equals(other.items, items)&&(identical(other.isTermsAccepted, isTermsAccepted) || other.isTermsAccepted == isTermsAccepted)&&(identical(other.applyClubPoints, applyClubPoints) || other.applyClubPoints == applyClubPoints)&&(identical(other.clubPoints, clubPoints) || other.clubPoints == clubPoints)&&(identical(other.showTermsError, showTermsError) || other.showTermsError == showTermsError)&&(identical(other.isPaymentProcessingNeeded, isPaymentProcessingNeeded) || other.isPaymentProcessingNeeded == isPaymentProcessingNeeded)&&(identical(other.paymentStatus, paymentStatus) || other.paymentStatus == paymentStatus));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,isLoading,cardState,roomNumber,const DeepCollectionEquality().hash(items),isTermsAccepted,applyClubPoints,clubPoints,showTermsError,isPaymentProcessingNeeded,paymentStatus);
+
+@override
+String toString() {
+ return 'CheckInState(isLoading: $isLoading, cardState: $cardState, roomNumber: $roomNumber, items: $items, isTermsAccepted: $isTermsAccepted, applyClubPoints: $applyClubPoints, clubPoints: $clubPoints, showTermsError: $showTermsError, isPaymentProcessingNeeded: $isPaymentProcessingNeeded, paymentStatus: $paymentStatus)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class $CheckInStateCopyWith<$Res> {
+ factory $CheckInStateCopyWith(CheckInState value, $Res Function(CheckInState) _then) = _$CheckInStateCopyWithImpl;
+@useResult
+$Res call({
+ bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus
+});
+
+
+
+
+}
+/// @nodoc
+class _$CheckInStateCopyWithImpl<$Res>
+ implements $CheckInStateCopyWith<$Res> {
+ _$CheckInStateCopyWithImpl(this._self, this._then);
+
+ final CheckInState _self;
+ final $Res Function(CheckInState) _then;
+
+/// Create a copy of CheckInState
+/// with the given fields replaced by the non-null parameter values.
+@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? cardState = null,Object? roomNumber = null,Object? items = null,Object? isTermsAccepted = null,Object? applyClubPoints = null,Object? clubPoints = null,Object? showTermsError = null,Object? isPaymentProcessingNeeded = null,Object? paymentStatus = null,}) {
+ return _then(_self.copyWith(
+isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
+as bool,cardState: null == cardState ? _self.cardState : cardState // ignore: cast_nullable_to_non_nullable
+as CheckInStatus,roomNumber: null == roomNumber ? _self.roomNumber : roomNumber // ignore: cast_nullable_to_non_nullable
+as String,items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
+as List<BookingAddonItem>,isTermsAccepted: null == isTermsAccepted ? _self.isTermsAccepted : isTermsAccepted // ignore: cast_nullable_to_non_nullable
+as bool,applyClubPoints: null == applyClubPoints ? _self.applyClubPoints : applyClubPoints // ignore: cast_nullable_to_non_nullable
+as bool,clubPoints: null == clubPoints ? _self.clubPoints : clubPoints // ignore: cast_nullable_to_non_nullable
+as int,showTermsError: null == showTermsError ? _self.showTermsError : showTermsError // ignore: cast_nullable_to_non_nullable
+as bool,isPaymentProcessingNeeded: null == isPaymentProcessingNeeded ? _self.isPaymentProcessingNeeded : isPaymentProcessingNeeded // ignore: cast_nullable_to_non_nullable
+as bool,paymentStatus: null == paymentStatus ? _self.paymentStatus : paymentStatus // ignore: cast_nullable_to_non_nullable
+as CheckInPaymentStatus,
+ ));
+}
+
+}
+
+
+/// Adds pattern-matching-related methods to [CheckInState].
+extension CheckInStatePatterns on CheckInState {
+/// A variant of `map` that fallback to returning `orElse`.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case _:
+/// return orElse();
+/// }
+/// ```
+
+@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CheckInState value)? $default,{required TResult orElse(),}){
+final _that = this;
+switch (_that) {
+case _CheckInState() when $default != null:
+return $default(_that);case _:
+ return orElse();
+
+}
+}
+/// A `switch`-like method, using callbacks.
+///
+/// Callbacks receives the raw object, upcasted.
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case final Subclass2 value:
+/// return ...;
+/// }
+/// ```
+
+@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CheckInState value) $default,){
+final _that = this;
+switch (_that) {
+case _CheckInState():
+return $default(_that);case _:
+ throw StateError('Unexpected subclass');
+
+}
+}
+/// A variant of `map` that fallback to returning `null`.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case _:
+/// return null;
+/// }
+/// ```
+
+@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CheckInState value)? $default,){
+final _that = this;
+switch (_that) {
+case _CheckInState() when $default != null:
+return $default(_that);case _:
+ return null;
+
+}
+}
+/// A variant of `when` that fallback to an `orElse` callback.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case _:
+/// return orElse();
+/// }
+/// ```
+
+@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus)? $default,{required TResult orElse(),}) {final _that = this;
+switch (_that) {
+case _CheckInState() when $default != null:
+return $default(_that.isLoading,_that.cardState,_that.roomNumber,_that.items,_that.isTermsAccepted,_that.applyClubPoints,_that.clubPoints,_that.showTermsError,_that.isPaymentProcessingNeeded,_that.paymentStatus);case _:
+ return orElse();
+
+}
+}
+/// A `switch`-like method, using callbacks.
+///
+/// As opposed to `map`, this offers destructuring.
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case Subclass2(:final field2):
+/// return ...;
+/// }
+/// ```
+
+@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus) $default,) {final _that = this;
+switch (_that) {
+case _CheckInState():
+return $default(_that.isLoading,_that.cardState,_that.roomNumber,_that.items,_that.isTermsAccepted,_that.applyClubPoints,_that.clubPoints,_that.showTermsError,_that.isPaymentProcessingNeeded,_that.paymentStatus);case _:
+ throw StateError('Unexpected subclass');
+
+}
+}
+/// A variant of `when` that fallback to returning `null`
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case _:
+/// return null;
+/// }
+/// ```
+
+@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus)? $default,) {final _that = this;
+switch (_that) {
+case _CheckInState() when $default != null:
+return $default(_that.isLoading,_that.cardState,_that.roomNumber,_that.items,_that.isTermsAccepted,_that.applyClubPoints,_that.clubPoints,_that.showTermsError,_that.isPaymentProcessingNeeded,_that.paymentStatus);case _:
+ return null;
+
+}
+}
+
+}
+
+/// @nodoc
+
+
+class _CheckInState extends CheckInState {
+ const _CheckInState({this.isLoading = false, this.cardState = CheckInStatus.loading, this.roomNumber = "", final List<BookingAddonItem> items = const [], this.isTermsAccepted = false, this.applyClubPoints = false, this.clubPoints = 0, this.showTermsError = false, this.isPaymentProcessingNeeded = true, this.paymentStatus = CheckInPaymentStatus.idle}): _items = items,super._();
+
+
+@override@JsonKey() final bool isLoading;
+@override@JsonKey() final CheckInStatus cardState;
+@override@JsonKey() final String roomNumber;
+// Payment-related fields
+ final List<BookingAddonItem> _items;
+// Payment-related fields
+@override@JsonKey() List<BookingAddonItem> get items {
+ if (_items is EqualUnmodifiableListView) return _items;
+ // ignore: implicit_dynamic_type
+ return EqualUnmodifiableListView(_items);
+}
+
+@override@JsonKey() final bool isTermsAccepted;
+@override@JsonKey() final bool applyClubPoints;
+@override@JsonKey() final int clubPoints;
+@override@JsonKey() final bool showTermsError;
+@override@JsonKey() final bool isPaymentProcessingNeeded;
+@override@JsonKey() final CheckInPaymentStatus paymentStatus;
+
+/// Create a copy of CheckInState
+/// with the given fields replaced by the non-null parameter values.
+@override @JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+_$CheckInStateCopyWith<_CheckInState> get copyWith => __$CheckInStateCopyWithImpl<_CheckInState>(this, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+ return identical(this, other) || (other.runtimeType == runtimeType&&other is _CheckInState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.cardState, cardState) || other.cardState == cardState)&&(identical(other.roomNumber, roomNumber) || other.roomNumber == roomNumber)&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.isTermsAccepted, isTermsAccepted) || other.isTermsAccepted == isTermsAccepted)&&(identical(other.applyClubPoints, applyClubPoints) || other.applyClubPoints == applyClubPoints)&&(identical(other.clubPoints, clubPoints) || other.clubPoints == clubPoints)&&(identical(other.showTermsError, showTermsError) || other.showTermsError == showTermsError)&&(identical(other.isPaymentProcessingNeeded, isPaymentProcessingNeeded) || other.isPaymentProcessingNeeded == isPaymentProcessingNeeded)&&(identical(other.paymentStatus, paymentStatus) || other.paymentStatus == paymentStatus));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,isLoading,cardState,roomNumber,const DeepCollectionEquality().hash(_items),isTermsAccepted,applyClubPoints,clubPoints,showTermsError,isPaymentProcessingNeeded,paymentStatus);
+
+@override
+String toString() {
+ return 'CheckInState(isLoading: $isLoading, cardState: $cardState, roomNumber: $roomNumber, items: $items, isTermsAccepted: $isTermsAccepted, applyClubPoints: $applyClubPoints, clubPoints: $clubPoints, showTermsError: $showTermsError, isPaymentProcessingNeeded: $isPaymentProcessingNeeded, paymentStatus: $paymentStatus)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class _$CheckInStateCopyWith<$Res> implements $CheckInStateCopyWith<$Res> {
+ factory _$CheckInStateCopyWith(_CheckInState value, $Res Function(_CheckInState) _then) = __$CheckInStateCopyWithImpl;
+@override @useResult
+$Res call({
+ bool isLoading, CheckInStatus cardState, String roomNumber, List<BookingAddonItem> items, bool isTermsAccepted, bool applyClubPoints, int clubPoints, bool showTermsError, bool isPaymentProcessingNeeded, CheckInPaymentStatus paymentStatus
+});
+
+
+
+
+}
+/// @nodoc
+class __$CheckInStateCopyWithImpl<$Res>
+ implements _$CheckInStateCopyWith<$Res> {
+ __$CheckInStateCopyWithImpl(this._self, this._then);
+
+ final _CheckInState _self;
+ final $Res Function(_CheckInState) _then;
+
+/// Create a copy of CheckInState
+/// with the given fields replaced by the non-null parameter values.
+@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? cardState = null,Object? roomNumber = null,Object? items = null,Object? isTermsAccepted = null,Object? applyClubPoints = null,Object? clubPoints = null,Object? showTermsError = null,Object? isPaymentProcessingNeeded = null,Object? paymentStatus = null,}) {
+ return _then(_CheckInState(
+isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
+as bool,cardState: null == cardState ? _self.cardState : cardState // ignore: cast_nullable_to_non_nullable
+as CheckInStatus,roomNumber: null == roomNumber ? _self.roomNumber : roomNumber // ignore: cast_nullable_to_non_nullable
+as String,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
+as List<BookingAddonItem>,isTermsAccepted: null == isTermsAccepted ? _self.isTermsAccepted : isTermsAccepted // ignore: cast_nullable_to_non_nullable
+as bool,applyClubPoints: null == applyClubPoints ? _self.applyClubPoints : applyClubPoints // ignore: cast_nullable_to_non_nullable
+as bool,clubPoints: null == clubPoints ? _self.clubPoints : clubPoints // ignore: cast_nullable_to_non_nullable
+as int,showTermsError: null == showTermsError ? _self.showTermsError : showTermsError // ignore: cast_nullable_to_non_nullable
+as bool,isPaymentProcessingNeeded: null == isPaymentProcessingNeeded ? _self.isPaymentProcessingNeeded : isPaymentProcessingNeeded // ignore: cast_nullable_to_non_nullable
+as bool,paymentStatus: null == paymentStatus ? _self.paymentStatus : paymentStatus // ignore: cast_nullable_to_non_nullable
+as CheckInPaymentStatus,
+ ));
+}
+
+
+}
+
+// dart format on
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
deleted file mode 100644
index fa261938..00000000
--- a/comwell_key_app/lib/check_in/bloc/check_in_cubit.dart
+++ /dev/null
@@ -1,212 +0,0 @@
-import 'package:comwell_key_app/base/base_cubit.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
-import 'package:comwell_key_app/check_in/check_in_repository.dart';
-import 'package:comwell_key_app/domain/repositories/booking_details_repository.dart';
-import 'package:comwell_key_app/domain/repositories/profile_repository.dart';
-import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/routing/app_routes.dart';
-import 'package:comwell_key_app/services/models/booking_dto.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/utils/locator.dart';
-import 'package:comwell_key_app/utils/urls.dart';
-import 'package:flutter/material.dart';
-import 'package:go_router/go_router.dart';
-import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
-import 'package:url_launcher/url_launcher.dart';
-
-class CheckInCubit extends BaseCubit<CheckInState> {
- final _checkInRepository = CheckInRepository();
- final ProfileRepository profileRepository = locator<ProfileRepository>();
- final BookingDetailsRepository bookingDetailsRepository = locator<BookingDetailsRepository>();
- final _tracking = locator<ComwellTracking>();
- final pageController = PageController();
- bool _isAnimating = false;
-
- late final Booking booking;
- PaymentCubit? paymentServicesCubit;
-
- CheckInCubit(this.booking) : super(const CheckInState()) {
- init();
- }
-
- CheckInCubit.withPayment(this.booking, this.paymentServicesCubit)
- : super(const CheckInState()) {
- initPaymentFlow();
- }
-
- CheckInCubit.initialOnlyKeys(this.booking) : super(const CheckInState()) {
- initOnlyKeys();
- }
-
- /// Initialize the payment flow: fetch club points, booking details, and add-on items
- Future<void> initPaymentFlow() async {
- try {
- safeEmit(state.copyWith(isLoading: true));
- final user = await profileRepository.fetchProfileSettings();
- safeEmit(state.copyWith(clubPoints: user.points));
-
- booking = await bookingDetailsRepository.getRemoteBookingDetails(
- booking.confirmationNumber,
- booking.hotelCode,
- );
-
- setItems(booking.addOnItems ?? []);
- safeEmit(state.copyWith(isLoading: false));
- } catch (e) {
- logError(e, StackTrace.current);
- safeEmit(state.copyWith(isLoading: false));
- }
- }
-
- void setItems(Iterable<BookingAddonItem> items) {
- final newItems = <BookingAddonItem>[];
- for (var item in items) {
- newItems.add(item.copyWith(price: item.price * item.quantity));
- }
- safeEmit(state.copyWith(items: newItems));
- }
-
- void onApplyClubPointsClicked(bool value) {
- safeEmit(state.copyWith(applyClubPoints: value));
- }
-
- void onAcceptTermsChanged(bool value) {
- if (value) {
- safeEmit(state.copyWith(isTermsAccepted: true, showTermsError: false));
- } else {
- safeEmit(state.copyWith(isTermsAccepted: false));
- }
- }
-
- void showTermsAndConditions() {
- launchUrl(Uri.parse(ComwellUrls.termsAndConditions));
- }
-
- void onContinueClicked(BuildContext context) {
- if (!state.isTermsAccepted && (booking.balance ?? 0.0) > 0.0) {
- safeEmit(state.copyWith(showTermsError: true));
- } else {
- if (booking.balance == 0 || booking.balance == null) {
- // No payment needed, go straight to check-in processing
- safeEmit(state.copyWith(isPaymentProcessingNeeded: false));
- _startCheckIn(context);
- } else {
- context.push(AppRoutes.paymentProcessing);
- processPayment();
- }
- }
- }
-
- Future<void> processPayment() async {
- final analyticsEventItem = AnalyticsEventItem(
- hotelName: booking.hotelName,
- currency: "DKK",
- value: booking.balance?.toInt() ?? 0,
- placement: "placement",
- items: booking.addOnItems?.map((e) => e.description).toList() ?? [],
- itemId: "itemId",
- itemName: "itemName",
- price: booking.balance?.toInt() ?? 0,
- quantity: 1,
- );
- _tracking.trackBeginCheckout(analyticsEventItem);
- try {
- await paymentServicesCubit?.createSession(
- booking.balance?.toInt() ?? 0,
- booking.confirmationNumber,
- state.applyClubPoints,
- booking.hotelCode,
- );
- await Future<void>.delayed(const Duration(milliseconds: 4000));
- } catch (e) {
- safeEmit(state.copyWith(paymentStatus: CheckInPaymentStatus.error));
- }
- }
-
- void _startCheckIn(BuildContext context) {
- context.push(AppRoutes.checkIn, extra: [booking, false]);
- }
-
- bool onBackPressed() {
- if (pageController.page == 0.0) return false;
- if (_isAnimating) return true;
- _isAnimating = true;
- pageController
- .previousPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn)
- .then((_) {
- _isAnimating = false;
- });
- return true;
- }
-
- /// Removes the decimal point from the balance if it is .0
- int get trimmedBalance {
- final balance = booking.balance;
- if (balance == null) return 0;
- return balance.toInt();
- }
-
- Future<void> initOnlyKeys() async {
- try {
- final bookingDetails = await _checkInRepository.getBookingDetails(
- booking.id,
- booking.hotelCode,
- );
- safeEmit(state.copyWith(roomNumber: bookingDetails.roomNumber));
- await _checkInRepository.checkIfSetup();
- await _checkInRepository.provisionKey(booking.id, booking.hotelCode);
- await Future<void>.delayed(const Duration(milliseconds: 3000));
- await _tryGetKeys();
- safeEmit(state.copyWith(roomNumber: bookingDetails.roomNumber));
- } catch (err, st) {
- logError(err, st);
- safeEmit(state.copyWith(cardState: CheckInStatus.error));
- }
- }
-
- Future<void> init() async {
- try {
- await Future<void>.delayed(const Duration(milliseconds: 500));
- safeEmit(state.copyWith(isLoading: true));
- await _checkInRepository.checkIn(booking.confirmationNumber);
- final bookingDetails = await _checkInRepository.getBookingDetails(
- booking.id,
- booking.hotelCode,
- );
- safeEmit(state.copyWith(cardState: CheckInStatus.roomFound));
- 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));
- safeEmit(state.copyWith(roomNumber: bookingDetails.roomNumber));
- } catch (err, st) {
- logError(err, st);
- safeEmit(state.copyWith(cardState: CheckInStatus.error));
- } finally {
- safeEmit(state.copyWith(isLoading: false));
- }
- }
-
- Future<void> _tryGetKeys({int attempt = 0}) async {
- try {
- await _checkInRepository.getKeys(booking.id, booking.hotelCode);
- } catch (e) {
- if (attempt < _getKeysRetryAttempts) {
- await Future<void>.delayed(const Duration(milliseconds: 500));
- await _tryGetKeys(attempt: attempt + 1);
- } else {
- rethrow;
- }
- }
- }
-
- @override
- Future<void> close() {
- pageController.dispose();
- return super.close();
- }
-
- static const _getKeysRetryAttempts = 3;
-}
diff --git a/comwell_key_app/lib/check_in/bloc/check_in_state.dart b/comwell_key_app/lib/check_in/bloc/check_in_state.dart
deleted file mode 100644
index e0a077f9..00000000
--- a/comwell_key_app/lib/check_in/bloc/check_in_state.dart
+++ /dev/null
@@ -1,80 +0,0 @@
-import 'package:comwell_key_app/services/models/booking_dto.dart';
-import 'package:comwell_key_app/utils/l10n_utils.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-
-part '../../.generated/check_in/bloc/check_in_state.freezed.dart';
-
-enum CheckInStatus {
- loading,
- roomFound,
- done,
- error,
-}
-
-enum CheckInPaymentStatus {
- idle,
- processing,
- success,
- error,
-}
-
-@freezed
-abstract class CheckInState with _$CheckInState {
- const factory CheckInState({
- @Default(false) bool isLoading,
- @Default(CheckInStatus.loading) CheckInStatus cardState,
- @Default("") String roomNumber,
- // Payment-related fields
- @Default([]) List<BookingAddonItem> items,
- @Default(false) bool isTermsAccepted,
- @Default(false) bool applyClubPoints,
- @Default(0) int clubPoints,
- @Default(false) bool showTermsError,
- @Default(true) bool isPaymentProcessingNeeded,
- @Default(CheckInPaymentStatus.idle) CheckInPaymentStatus paymentStatus,
- }) = _CheckInState;
-
- const CheckInState._();
-
- int get totalPriceBeforeDiscount => _sumOfList(items);
-
- int get totalPriceAfterDiscount => _sumOfList(itemsWithDiscount);
-
- Iterable<BookingAddonItem> get itemsWithDiscount {
- if (applyClubPoints) {
- return [
- ...items,
- BookingAddonItem("discount", "discount", clubPoints * -1, clubPoints * -1),
- ];
- }
- return items;
- }
-
- int _sumOfList(Iterable<BookingAddonItem> list) {
- if (list.isEmpty) return 0;
- return list.map((item) => item.price).reduce((total, price) => total + price);
- }
-
- String titleStringId(BuildContext context) {
- switch (cardState) {
- case CheckInStatus.loading:
- return context.strings.check_in_loading_title;
- case CheckInStatus.roomFound:
- return context.strings.check_in_room_found_title;
- case CheckInStatus.done:
- return context.strings.check_in_your_digital_card_title;
- case CheckInStatus.error:
- return context.strings.check_in_error_title;
- }
- }
-
- String subtitleStringId(BuildContext context) {
- switch (cardState) {
- case CheckInStatus.done:
- return context.strings.check_in_your_digital_card_subtitle;
- default:
- return "";
- }
- }
-}
diff --git a/comwell_key_app/lib/check_in/check_in_flow.dart b/comwell_key_app/lib/check_in/check_in_flow.dart
deleted file mode 100644
index 7f027f35..00000000
--- a/comwell_key_app/lib/check_in/check_in_flow.dart
+++ /dev/null
@@ -1,62 +0,0 @@
-import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
-import 'package:comwell_key_app/check_in/components/check_in_bottom_sheet.dart';
-import 'package:comwell_key_app/check_in/pages/check_in_page_enum.dart';
-import 'package:comwell_key_app/check_in/pages/check_in_processing_page.dart';
-import 'package:comwell_key_app/common/components/comwell_app_bar.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';
-import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
-import 'package:payment_plugin/presentation/app/bloc/payment_processing_state.dart';
-
-class CheckInFlow extends StatelessWidget {
- const CheckInFlow({super.key});
-
- @override
- Widget build(BuildContext context) {
- final cubit = context.read<CheckInCubit>();
- if (!cubit.state.isPaymentProcessingNeeded) {
- return const CheckInProcessingPage();
- } else {
- return BlocListener<PaymentCubit, PaymentProcessingState>(
- listenWhen: (previous, current) =>
- previous is! PaymentProcessingStateConfirmed &&
- current is PaymentProcessingStateConfirmed,
- 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: 1));
- // After payment, navigate to the actual check-in processing
- if (context.mounted) {
- context.push(AppRoutes.checkIn, extra: [cubit.booking, false]);
- }
- }
- },
- child: BlocBuilder<CheckInCubit, CheckInState>(
- builder: (context, state) {
- return Scaffold(
- appBar: ComwellAppBar(
- shouldShowProfileButton: false,
- onBackPressed: () {
- final didScroll = cubit.onBackPressed();
- if (!didScroll) context.pop();
- },
- ),
- bottomSheet: CheckInBottomSheet(state: state),
- backgroundColor: Colors.white,
- body: PageView(
- controller: cubit.pageController,
- key: const PageStorageKey("check_in_flow"),
- physics: const NeverScrollableScrollPhysics(),
- children:
- CheckInFlowPage.getPages(ValueKey(state)).toList(),
- ),
- );
- },
- ),
- );
- }
- }
-}
diff --git a/comwell_key_app/lib/check_in/check_in_page.dart b/comwell_key_app/lib/check_in/check_in_page.dart
deleted file mode 100644
index ee325466..00000000
--- a/comwell_key_app/lib/check_in/check_in_page.dart
+++ /dev/null
@@ -1,270 +0,0 @@
-import 'package:comwell_key_app/.generated/assets/assets.gen.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
-import 'package:comwell_key_app/common/components/comwell_card_component.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:comwell_key_app/utils/l10n_utils.dart';
-import 'package:comwell_key_app/utils/lottie_utils.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';
-
-class CheckInPage extends StatefulWidget {
- const CheckInPage({super.key});
-
- @override
- State<CheckInPage> createState() => _CheckInPageState();
-}
-
-class _CheckInPageState extends State<CheckInPage> with SingleTickerProviderStateMixin {
- LottieComposition? loadingComposition;
- late final AnimationController animationController;
- late final Widget loadingAnimation;
-
- @override
- void initState() {
- animationController = AnimationController(vsync: this);
- loadingAnimation = Lottie.asset(
- Assets.animations.loadAnimation,
- controller: animationController,
- onLoaded: (composition) {
- if (loadingComposition == null) {
- loadingComposition = composition;
- animationController.duration = composition.duration;
- }
- },
- fit: BoxFit.cover,
- width: 64,
- height: 64,
- );
- super.initState();
- }
-
- @override
- void dispose() {
- animationController.dispose();
- super.dispose();
- }
-
- void onAnimationEnd() {
- playLoading();
- }
-
- void playLoading() {
- loadingComposition?.playBetween(
- animationController,
- "spinner",
- markerEnd: "success",
- repeat: true,
- );
- }
-
- void playError() {
- loadingComposition?.playBetween(animationController, "error");
- }
-
- void playSuccess() {
- loadingComposition?.playBetween(animationController, "success", markerEnd: "error");
- }
-
- double getCardPosition(BuildContext context) {
- final mq = MediaQuery.of(context);
- final cubit = context.read<CheckInCubit>();
- final state = cubit.state;
- final padding = mq.viewPadding;
- final height = mq.size.height;
- final startPosition = height - padding.bottom - padding.top - 100;
- switch (state.cardState) {
- case CheckInStatus.loading:
- case CheckInStatus.error:
- return height / 2 - 100;
- case CheckInStatus.done:
- return 100;
- case _:
- return startPosition;
- }
- }
-
- Widget getCardContent(BuildContext context) {
- final cubit = context.read<CheckInCubit>();
- if (cubit.state.cardState == CheckInStatus.done) {
- return Container(
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.all(Radius.circular(30)),
- border: Border.all(color: colorBackground, width: 2),
- ),
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Text(
- "${context.strings.check_in_your_digital_card_room_prefix} ${(cubit.booking.roomNumber)}",
- style: Theme.of(context).textTheme.headlineLarge?.copyWith(
- color: colorBackground,
- ),
- ),
- ),
- );
- } else {
- return loadingAnimation;
- }
- }
-
- @override
- Widget build(BuildContext context) {
- final cubit = context.read<CheckInCubit>();
- final theme = Theme.of(context);
-
- return BlocConsumer<CheckInCubit, CheckInState>(
- listener: (context, state) {
- switch (state.cardState) {
- case CheckInStatus.loading:
- playLoading();
- break;
- case CheckInStatus.roomFound:
- playSuccess();
- break;
- case CheckInStatus.error:
- playError();
- break;
- default:
- break;
- }
- },
- builder: (context, state) {
- return Scaffold(
- body: SafeArea(
- child: Builder(
- builder: (context) {
- final position = getCardPosition(context);
- return Stack(
- fit: StackFit.expand,
- children: [
- AnimatedPositioned(
- duration: const Duration(milliseconds: 200),
- top: position,
- left: 16,
- right: 16,
- curve: Easing.linear,
- onEnd: onAnimationEnd,
- child: ComwellCard(content: getCardContent(context)),
- ),
- if (state.cardState == CheckInStatus.error)
- Align(
- alignment: Alignment.bottomCenter,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- const SizedBox(height: 15),
- Text(
- context.strings.check_in_page_error_title,
- textAlign: TextAlign.center,
- style: theme.textTheme.headlineMedium?.copyWith(
- color: colorHeadlineText,
- ),
- ),
- const SizedBox(height: 15),
- Text(
- context.strings.check_in_page_error_subtitle,
- textAlign: TextAlign.center,
- style: theme.textTheme.bodySmall?.copyWith(color: colorHeadlineText),
- ),
- const SizedBox(height: 15),
- Text(state.subtitleStringId(context), style: theme.textTheme.bodySmall),
- const SizedBox(height: 15),
- const Divider(
- color: colorDivider,
- height: 0,
- ),
- Row(
- children: [
- Expanded(
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: ElevatedButton(
- onPressed: () {
- context.pop();
- },
- style: ButtonStyle(
- backgroundColor: WidgetStatePropertyAll(sandColor[80]),
- foregroundColor: const WidgetStatePropertyAll(
- colorBackground,
- ),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 16.0),
- child: Text(context.strings.generic_continue),
- ),
- ),
- ),
- ),
- ],
- ),
- ],
- ),
- ),
- if (state.cardState == CheckInStatus.done)
- Align(
- alignment: Alignment.bottomCenter,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: Text(
- context.strings.check_in_your_digital_card_nb,
- textAlign: TextAlign.center,
- style: theme.textTheme.bodySmall?.copyWith(
- color: colorHeadlineText,
- ),
- ),
- ),
- const SizedBox(height: 15),
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: Text(
- state.subtitleStringId(context),
- style: theme.textTheme.bodySmall,
- ),
- ),
- const SizedBox(height: 15),
- const Divider(
- color: colorDivider,
- height: 0,
- ),
- Row(
- children: [
- Expanded(
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: ElevatedButton(
- onPressed: () {
- context.pop(true);
- },
- style: ButtonStyle(
- backgroundColor: WidgetStatePropertyAll(sandColor[80]),
- foregroundColor: const WidgetStatePropertyAll(
- colorBackground,
- ),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 16.0),
- child: Text(context.strings.generic_done),
- ),
- ),
- ),
- ),
- ],
- ),
- ],
- ),
- ),
- ],
- );
- },
- ),
- ),
- );
- },
- );
- }
-}
diff --git a/comwell_key_app/lib/check_in/check_in_repository.dart b/comwell_key_app/lib/check_in/check_in_repository.dart
deleted file mode 100644
index 6a81924a..00000000
--- a/comwell_key_app/lib/check_in/check_in_repository.dart
+++ /dev/null
@@ -1,83 +0,0 @@
-
-import 'package:comwell_key_app/database/comwell_db.dart';
-import 'package:comwell_key_app/domain/repositories/booking_details_repository.dart';
-import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/domain/repositories/profile_repository.dart';
-import 'package:comwell_key_app/services/api.dart';
-import 'package:comwell_key_app/utils/locator.dart';
-import 'package:dio/dio.dart';
-import 'package:flutter/cupertino.dart';
-
-import '../utils/seos_repository.dart';
-
-class CheckInRepository {
- final api = Api();
- final db = locator.get<ComwellDatabase>();
- final seosRepository = locator<SeosRepository>();
- final profileRepository = locator<ProfileRepository>();
- final bookingDetailsRepository = locator<BookingDetailsRepository>();
-
- Future<dynamic> checkIn(String confirmationNumber) async {
- try {
- final response = await api.checkIn(confirmationNumber);
- return response;
- } on DioException catch (err) {
- if (err.response?.data != null) {
- final errorData = err.response?.data as Map<String, dynamic>;
- throw Exception("${errorData['title']}: ${errorData['detail']}");
- }
- throw Exception("Failed to check in: ${err.message}");
- } catch (err, st) {
- debugPrint("error in checkInRepository: $err, $st");
- throw Exception("An unexpected error occurred during check-in");
- }
- }
-
- Future<Booking> getBookingDetails(String hmsConfirmationNumber, String hotelCode) async {
- final booking = await bookingDetailsRepository.getBookingDetails(hmsConfirmationNumber, hotelCode);
- return booking;
- }
-
- Future<void> checkIfSetup() async {
- final isSetup = await seosRepository.isEndpointSetup();
- if (!isSetup) await seosRepository.startMobilePlugin();
- }
-
- 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(),
- confirmationNumber: "confirmationNumber",
- reservationStatus: ReservationStatus.newreservation,
- image: "",
- hotelName: "hotelName",
- roomType: "roomType",
- children: 0,
- adults: 2,
- hotelCode: "hotelCode",
- balance: 100,
- maskedCardNumber: "1234567890",
- bookingDate: DateTime.now(),
- digitalCard: true,
- guests: null,
- addOnItems: const [],
- );
-}
diff --git a/comwell_key_app/lib/check_in/check_in_route.dart b/comwell_key_app/lib/check_in/check_in_route.dart
deleted file mode 100644
index 0e8133e9..00000000
--- a/comwell_key_app/lib/check_in/check_in_route.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
-import 'package:comwell_key_app/check_in/check_in_flow.dart';
-import 'package:comwell_key_app/check_in/check_in_page.dart';
-import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/routing/app_routes.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:go_router/go_router.dart';
-
-final checkInRoute = GoRoute(
- path: AppRoutes.checkIn,
- 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(),
- );
- },
-);
-
-final checkInPaymentRoute = GoRoute(
- path: AppRoutes.checkInPayment,
- builder: (context, state) {
- final booking = state.extra as Booking;
- return BlocProvider(
- create: (context) => CheckInCubit.withPayment(booking, context.read()),
- child: BlocBuilder<CheckInCubit, CheckInState>(
- builder: (context, checkInState) {
- return CheckInFlow(key: ValueKey(checkInState));
- },
- ),
- );
- },
-);
diff --git a/comwell_key_app/lib/check_in/components/check_in_bottom_sheet.dart b/comwell_key_app/lib/check_in/components/check_in_bottom_sheet.dart
deleted file mode 100644
index 0e61f1b6..00000000
--- a/comwell_key_app/lib/check_in/components/check_in_bottom_sheet.dart
+++ /dev/null
@@ -1,63 +0,0 @@
-import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:comwell_key_app/utils/l10n_utils.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class CheckInBottomSheet extends StatelessWidget {
- final CheckInState state;
- const CheckInBottomSheet({super.key, required this.state});
-
- @override
- Widget build(BuildContext context) {
- final cubit = context.read<CheckInCubit>();
- final theme = Theme.of(context);
- final isEnabled = state.isTermsAccepted;
-
- final buttonStyle = ButtonStyle(
- backgroundColor: WidgetStateProperty.resolveWith((states) {
- if (states.contains(WidgetState.disabled)) {
- return sandColor.withValues(alpha: 0.5);
- }
- return sandColor;
- }),
- foregroundColor: const WidgetStatePropertyAll(colorBackground),
- );
-
- return Container(
- decoration: const BoxDecoration(
- shape: BoxShape.rectangle, color: colorBackground),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Divider(color: colorDivider),
- Padding(
- padding: const EdgeInsets.only(
- left: 18.0, right: 18.0, bottom: 32.0, top: 18.0),
- child: Row(
- children: [
- Expanded(
- child: ElevatedButton(
- onPressed: isEnabled
- ? () => cubit.onContinueClicked(context)
- : null,
- style: buttonStyle,
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 17.0),
- child: Text(
- context.strings.checkout_page_payment_payment_title,
- style: theme.textTheme.bodyMedium
- ?.copyWith(color: colorBackground),
- ),
- )),
- ),
- ],
- ),
- ),
- const SizedBox(height: 8)
- ],
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/check_in/pages/check_in_page_enum.dart b/comwell_key_app/lib/check_in/pages/check_in_page_enum.dart
deleted file mode 100644
index 0d51cbc2..00000000
--- a/comwell_key_app/lib/check_in/pages/check_in_page_enum.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-import 'package:flutter/cupertino.dart';
-
-import 'check_in_payment_page.dart';
-
-enum CheckInFlowPage {
- payment;
-
- static CheckInFlowPage fromIndex(int index) {
- return CheckInFlowPage.values[index];
- }
-
- static Iterable<Widget> getPages(Key key) {
- return CheckInFlowPage.values.map((page) {
- switch (page) {
- case CheckInFlowPage.payment:
- return CheckInPaymentPage(key: key);
- }
- });
- }
-}
diff --git a/comwell_key_app/lib/check_in/pages/check_in_payment_page.dart b/comwell_key_app/lib/check_in/pages/check_in_payment_page.dart
deleted file mode 100644
index c34e2470..00000000
--- a/comwell_key_app/lib/check_in/pages/check_in_payment_page.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
-import 'package:comwell_key_app/common/template_pages/payment_page_template.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class CheckInPaymentPage extends StatelessWidget {
- const CheckInPaymentPage({super.key});
-
- @override
- Widget build(BuildContext context) {
- final cubit = context.read<CheckInCubit>();
- final addOnItems = cubit.state.items;
- final applyClubPoints = cubit.state.applyClubPoints;
- final totalPriceBeforeDiscount = cubit.state.totalPriceBeforeDiscount;
- final totalPriceAfterDiscount = cubit.state.totalPriceAfterDiscount;
- final trimmedBalance = cubit.trimmedBalance;
- final clubPoints = cubit.state.clubPoints;
- final isTermsAccepted = cubit.state.isTermsAccepted;
- final onAcceptTermsChanged = cubit.onAcceptTermsChanged;
- final showError = cubit.state.showTermsError;
- final onShowTermsAndConditions = cubit.showTermsAndConditions;
- final onApplyClubPointsChanged = cubit.onApplyClubPointsClicked;
-
- return PaymentPageTemplate(
- addOnItems: addOnItems,
- applyClubPoints: applyClubPoints,
- totalPriceBeforeDiscount: totalPriceBeforeDiscount,
- totalPriceAfterDiscount: totalPriceAfterDiscount,
- trimmedBalance: trimmedBalance,
- clubPoints: clubPoints,
- isTermsAccepted: isTermsAccepted,
- userEmail: cubit.booking.bookerLastName,
- onAcceptTermsChanged: onAcceptTermsChanged,
- showError: showError,
- onShowTermsAndConditions: onShowTermsAndConditions,
- onApplyClubPointsChanged: onApplyClubPointsChanged);
- }
-}
diff --git a/comwell_key_app/lib/check_in/pages/check_in_processing_page.dart b/comwell_key_app/lib/check_in/pages/check_in_processing_page.dart
deleted file mode 100644
index 12395450..00000000
--- a/comwell_key_app/lib/check_in/pages/check_in_processing_page.dart
+++ /dev/null
@@ -1,102 +0,0 @@
-import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
-import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
-import 'package:comwell_key_app/themes/dark_theme.dart';
-import 'package:comwell_key_app/utils/lottie_utils.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:lottie/lottie.dart';
-
-class CheckInProcessingPage extends StatefulWidget {
- const CheckInProcessingPage({super.key});
-
- @override
- State<CheckInProcessingPage> createState() => _CheckInProcessingPageState();
-}
-
-class _CheckInProcessingPageState extends State<CheckInProcessingPage>
- with SingleTickerProviderStateMixin {
- LottieComposition? loadingComposition;
- late final AnimationController animationController;
-
- @override
- void initState() {
- animationController = AnimationController(vsync: this);
- super.initState();
- }
-
- @override
- void dispose() {
- animationController.dispose();
- super.dispose();
- }
-
- void onAnimationEnd() {
- playLoading();
- }
-
- void playLoading() {
- loadingComposition?.playBetween(
- animationController,
- "spinner",
- markerEnd: "success",
- repeat: true,
- );
- }
-
- void playError() async {
- loadingComposition?.playBetween(
- animationController,
- "error",
- repeat: true,
- );
- await Future<void>.delayed(const Duration(seconds: 1));
- if (mounted) Navigator.of(context).pop();
- }
-
- void playSuccess() async {
- await loadingComposition?.playBetween(
- animationController,
- "success",
- markerEnd: "error",
- );
- await Future<void>.delayed(const Duration(seconds: 1));
- if (mounted) Navigator.of(context).pop();
- }
-
- @override
- Widget build(BuildContext context) {
- return BlocListener<CheckInCubit, CheckInState>(
- listenWhen: (previous, current) =>
- previous.paymentStatus != current.paymentStatus,
- listener: (context, state) {
- if (state.paymentStatus == CheckInPaymentStatus.success) {
- playSuccess();
- } else if (state.paymentStatus == CheckInPaymentStatus.error) {
- playError();
- }
- },
- child: Scaffold(
- body: Container(
- alignment: Alignment.center,
- color: sandColor[80],
- child: Builder(builder: (context) {
- return Lottie.asset(
- 'assets/animations/load_animation.json',
- controller: animationController,
- onLoaded: (composition) {
- if (loadingComposition == null) {
- loadingComposition = composition;
- animationController.duration = composition.duration;
- playLoading();
- }
- },
- fit: BoxFit.cover,
- width: 64,
- height: 64,
- );
- }),
- ),
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/domain/repositories/check_in_repository.dart b/comwell_key_app/lib/domain/repositories/check_in_repository.dart
new file mode 100644
index 00000000..6dda3ecc
--- /dev/null
+++ b/comwell_key_app/lib/domain/repositories/check_in_repository.dart
@@ -0,0 +1,82 @@
+
+import 'package:comwell_key_app/database/comwell_db.dart';
+import 'package:comwell_key_app/domain/repositories/booking_details_repository.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/domain/repositories/profile_repository.dart';
+import 'package:comwell_key_app/services/api.dart';
+import 'package:comwell_key_app/utils/locator.dart';
+import 'package:comwell_key_app/utils/seos_repository.dart';
+import 'package:dio/dio.dart';
+import 'package:flutter/cupertino.dart';
+
+class CheckInRepository {
+ final api = Api();
+ final db = locator.get<ComwellDatabase>();
+ final seosRepository = locator<SeosRepository>();
+ final profileRepository = locator<ProfileRepository>();
+ final bookingDetailsRepository = locator<BookingDetailsRepository>();
+
+ Future<dynamic> checkIn(String confirmationNumber) async {
+ try {
+ final response = await api.checkIn(confirmationNumber);
+ return response;
+ } on DioException catch (err) {
+ if (err.response?.data != null) {
+ final errorData = err.response?.data as Map<String, dynamic>;
+ throw Exception("${errorData['title']}: ${errorData['detail']}");
+ }
+ throw Exception("Failed to check in: ${err.message}");
+ } catch (err, st) {
+ debugPrint("error in checkInRepository: $err, $st");
+ throw Exception("An unexpected error occurred during check-in");
+ }
+ }
+
+ Future<Booking> getBookingDetails(String hmsConfirmationNumber, String hotelCode) async {
+ final booking = await bookingDetailsRepository.getBookingDetails(hmsConfirmationNumber, hotelCode);
+ return booking;
+ }
+
+ Future<void> checkIfSetup() async {
+ final isSetup = await seosRepository.isEndpointSetup();
+ if (!isSetup) await seosRepository.startMobilePlugin();
+ }
+
+ 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(),
+ confirmationNumber: "confirmationNumber",
+ reservationStatus: ReservationStatus.newreservation,
+ image: "",
+ hotelName: "hotelName",
+ roomType: "roomType",
+ children: 0,
+ adults: 2,
+ hotelCode: "hotelCode",
+ balance: 100,
+ maskedCardNumber: "1234567890",
+ bookingDate: DateTime.now(),
+ digitalCard: true,
+ guests: null,
+ addOnItems: const [],
+ );
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/bloc/check_in_cubit.dart b/comwell_key_app/lib/presentation/screens/check_in/bloc/check_in_cubit.dart
new file mode 100644
index 00000000..cbe38152
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/bloc/check_in_cubit.dart
@@ -0,0 +1,212 @@
+import 'package:comwell_key_app/base/base_cubit.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/domain/repositories/check_in_repository.dart';
+import 'package:comwell_key_app/domain/repositories/booking_details_repository.dart';
+import 'package:comwell_key_app/domain/repositories/profile_repository.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/routing/app_routes.dart';
+import 'package:comwell_key_app/services/models/booking_dto.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/utils/locator.dart';
+import 'package:comwell_key_app/utils/urls.dart';
+import 'package:flutter/material.dart';
+import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class CheckInCubit extends BaseCubit<CheckInState> {
+ final _checkInRepository = CheckInRepository();
+ final ProfileRepository profileRepository = locator<ProfileRepository>();
+ final BookingDetailsRepository bookingDetailsRepository = locator<BookingDetailsRepository>();
+ final _tracking = locator<ComwellTracking>();
+ final pageController = PageController();
+ bool _isAnimating = false;
+
+ late final Booking booking;
+ PaymentCubit? paymentServicesCubit;
+
+ CheckInCubit(this.booking) : super(const CheckInState()) {
+ init();
+ }
+
+ CheckInCubit.withPayment(this.booking, this.paymentServicesCubit)
+ : super(const CheckInState()) {
+ initPaymentFlow();
+ }
+
+ CheckInCubit.initialOnlyKeys(this.booking) : super(const CheckInState()) {
+ initOnlyKeys();
+ }
+
+ /// Initialize the payment flow: fetch club points, booking details, and add-on items
+ Future<void> initPaymentFlow() async {
+ try {
+ safeEmit(state.copyWith(isLoading: true));
+ final user = await profileRepository.fetchProfileSettings();
+ safeEmit(state.copyWith(clubPoints: user.points));
+
+ booking = await bookingDetailsRepository.getRemoteBookingDetails(
+ booking.confirmationNumber,
+ booking.hotelCode,
+ );
+
+ setItems(booking.addOnItems ?? []);
+ safeEmit(state.copyWith(isLoading: false));
+ } catch (e) {
+ logError(e, StackTrace.current);
+ safeEmit(state.copyWith(isLoading: false));
+ }
+ }
+
+ void setItems(Iterable<BookingAddonItem> items) {
+ final newItems = <BookingAddonItem>[];
+ for (var item in items) {
+ newItems.add(item.copyWith(price: item.price * item.quantity));
+ }
+ safeEmit(state.copyWith(items: newItems));
+ }
+
+ void onApplyClubPointsClicked(bool value) {
+ safeEmit(state.copyWith(applyClubPoints: value));
+ }
+
+ void onAcceptTermsChanged(bool value) {
+ if (value) {
+ safeEmit(state.copyWith(isTermsAccepted: true, showTermsError: false));
+ } else {
+ safeEmit(state.copyWith(isTermsAccepted: false));
+ }
+ }
+
+ void showTermsAndConditions() {
+ launchUrl(Uri.parse(ComwellUrls.termsAndConditions));
+ }
+
+ void onContinueClicked(BuildContext context) {
+ if (!state.isTermsAccepted && (booking.balance ?? 0.0) > 0.0) {
+ safeEmit(state.copyWith(showTermsError: true));
+ } else {
+ if (booking.balance == 0 || booking.balance == null) {
+ // No payment needed, go straight to check-in processing
+ safeEmit(state.copyWith(isPaymentProcessingNeeded: false));
+ _startCheckIn(context);
+ } else {
+ context.push(AppRoutes.paymentProcessing);
+ processPayment();
+ }
+ }
+ }
+
+ Future<void> processPayment() async {
+ final analyticsEventItem = AnalyticsEventItem(
+ hotelName: booking.hotelName,
+ currency: "DKK",
+ value: booking.balance?.toInt() ?? 0,
+ placement: "placement",
+ items: booking.addOnItems?.map((e) => e.description).toList() ?? [],
+ itemId: "itemId",
+ itemName: "itemName",
+ price: booking.balance?.toInt() ?? 0,
+ quantity: 1,
+ );
+ _tracking.trackBeginCheckout(analyticsEventItem);
+ try {
+ await paymentServicesCubit?.createSession(
+ booking.balance?.toInt() ?? 0,
+ booking.confirmationNumber,
+ state.applyClubPoints,
+ booking.hotelCode,
+ );
+ await Future<void>.delayed(const Duration(milliseconds: 4000));
+ } catch (e) {
+ safeEmit(state.copyWith(paymentStatus: CheckInPaymentStatus.error));
+ }
+ }
+
+ void _startCheckIn(BuildContext context) {
+ context.push(AppRoutes.checkIn, extra: [booking, false]);
+ }
+
+ bool onBackPressed() {
+ if (pageController.page == 0.0) return false;
+ if (_isAnimating) return true;
+ _isAnimating = true;
+ pageController
+ .previousPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn)
+ .then((_) {
+ _isAnimating = false;
+ });
+ return true;
+ }
+
+ /// Removes the decimal point from the balance if it is .0
+ int get trimmedBalance {
+ final balance = booking.balance;
+ if (balance == null) return 0;
+ return balance.toInt();
+ }
+
+ Future<void> initOnlyKeys() async {
+ try {
+ final bookingDetails = await _checkInRepository.getBookingDetails(
+ booking.id,
+ booking.hotelCode,
+ );
+ safeEmit(state.copyWith(roomNumber: bookingDetails.roomNumber));
+ await _checkInRepository.checkIfSetup();
+ await _checkInRepository.provisionKey(booking.id, booking.hotelCode);
+ await Future<void>.delayed(const Duration(milliseconds: 3000));
+ await _tryGetKeys();
+ safeEmit(state.copyWith(roomNumber: bookingDetails.roomNumber));
+ } catch (err, st) {
+ logError(err, st);
+ safeEmit(state.copyWith(cardState: CheckInStatus.error));
+ }
+ }
+
+ Future<void> init() async {
+ try {
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+ safeEmit(state.copyWith(isLoading: true));
+ await _checkInRepository.checkIn(booking.confirmationNumber);
+ final bookingDetails = await _checkInRepository.getBookingDetails(
+ booking.id,
+ booking.hotelCode,
+ );
+ safeEmit(state.copyWith(cardState: CheckInStatus.roomFound));
+ 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));
+ safeEmit(state.copyWith(roomNumber: bookingDetails.roomNumber));
+ } catch (err, st) {
+ logError(err, st);
+ safeEmit(state.copyWith(cardState: CheckInStatus.error));
+ } finally {
+ safeEmit(state.copyWith(isLoading: false));
+ }
+ }
+
+ Future<void> _tryGetKeys({int attempt = 0}) async {
+ try {
+ await _checkInRepository.getKeys(booking.id, booking.hotelCode);
+ } catch (e) {
+ if (attempt < _getKeysRetryAttempts) {
+ await Future<void>.delayed(const Duration(milliseconds: 500));
+ await _tryGetKeys(attempt: attempt + 1);
+ } else {
+ rethrow;
+ }
+ }
+ }
+
+ @override
+ Future<void> close() {
+ pageController.dispose();
+ return super.close();
+ }
+
+ static const _getKeysRetryAttempts = 3;
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/bloc/check_in_state.dart b/comwell_key_app/lib/presentation/screens/check_in/bloc/check_in_state.dart
new file mode 100644
index 00000000..175b86dd
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/bloc/check_in_state.dart
@@ -0,0 +1,80 @@
+import 'package:comwell_key_app/services/models/booking_dto.dart';
+import 'package:comwell_key_app/utils/l10n_utils.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part '../../../../.generated/presentation/screens/check_in/bloc/check_in_state.freezed.dart';
+
+enum CheckInStatus {
+ loading,
+ roomFound,
+ done,
+ error,
+}
+
+enum CheckInPaymentStatus {
+ idle,
+ processing,
+ success,
+ error,
+}
+
+@freezed
+abstract class CheckInState with _$CheckInState {
+ const factory CheckInState({
+ @Default(false) bool isLoading,
+ @Default(CheckInStatus.loading) CheckInStatus cardState,
+ @Default("") String roomNumber,
+ // Payment-related fields
+ @Default([]) List<BookingAddonItem> items,
+ @Default(false) bool isTermsAccepted,
+ @Default(false) bool applyClubPoints,
+ @Default(0) int clubPoints,
+ @Default(false) bool showTermsError,
+ @Default(true) bool isPaymentProcessingNeeded,
+ @Default(CheckInPaymentStatus.idle) CheckInPaymentStatus paymentStatus,
+ }) = _CheckInState;
+
+ const CheckInState._();
+
+ int get totalPriceBeforeDiscount => _sumOfList(items);
+
+ int get totalPriceAfterDiscount => _sumOfList(itemsWithDiscount);
+
+ Iterable<BookingAddonItem> get itemsWithDiscount {
+ if (applyClubPoints) {
+ return [
+ ...items,
+ BookingAddonItem("discount", "discount", clubPoints * -1, clubPoints * -1),
+ ];
+ }
+ return items;
+ }
+
+ int _sumOfList(Iterable<BookingAddonItem> list) {
+ if (list.isEmpty) return 0;
+ return list.map((item) => item.price).reduce((total, price) => total + price);
+ }
+
+ String titleStringId(BuildContext context) {
+ switch (cardState) {
+ case CheckInStatus.loading:
+ return context.strings.check_in_loading_title;
+ case CheckInStatus.roomFound:
+ return context.strings.check_in_room_found_title;
+ case CheckInStatus.done:
+ return context.strings.check_in_your_digital_card_title;
+ case CheckInStatus.error:
+ return context.strings.check_in_error_title;
+ }
+ }
+
+ String subtitleStringId(BuildContext context) {
+ switch (cardState) {
+ case CheckInStatus.done:
+ return context.strings.check_in_your_digital_card_subtitle;
+ default:
+ return "";
+ }
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/check_in_flow.dart b/comwell_key_app/lib/presentation/screens/check_in/check_in_flow.dart
new file mode 100644
index 00000000..c07aac9b
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/check_in_flow.dart
@@ -0,0 +1,62 @@
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_cubit.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/components/check_in_bottom_sheet.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/pages/check_in_page_enum.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/pages/check_in_processing_page.dart';
+import 'package:comwell_key_app/common/components/comwell_app_bar.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';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_processing_state.dart';
+
+class CheckInFlow extends StatelessWidget {
+ const CheckInFlow({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final cubit = context.read<CheckInCubit>();
+ if (!cubit.state.isPaymentProcessingNeeded) {
+ return const CheckInProcessingPage();
+ } else {
+ return BlocListener<PaymentCubit, PaymentProcessingState>(
+ listenWhen: (previous, current) =>
+ previous is! PaymentProcessingStateConfirmed &&
+ current is PaymentProcessingStateConfirmed,
+ 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: 1));
+ // After payment, navigate to the actual check-in processing
+ if (context.mounted) {
+ context.push(AppRoutes.checkIn, extra: [cubit.booking, false]);
+ }
+ }
+ },
+ child: BlocBuilder<CheckInCubit, CheckInState>(
+ builder: (context, state) {
+ return Scaffold(
+ appBar: ComwellAppBar(
+ shouldShowProfileButton: false,
+ onBackPressed: () {
+ final didScroll = cubit.onBackPressed();
+ if (!didScroll) context.pop();
+ },
+ ),
+ bottomSheet: CheckInBottomSheet(state: state),
+ backgroundColor: Colors.white,
+ body: PageView(
+ controller: cubit.pageController,
+ key: const PageStorageKey("check_in_flow"),
+ physics: const NeverScrollableScrollPhysics(),
+ children:
+ CheckInFlowPage.getPages(ValueKey(state)).toList(),
+ ),
+ );
+ },
+ ),
+ );
+ }
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/check_in_page.dart b/comwell_key_app/lib/presentation/screens/check_in/check_in_page.dart
new file mode 100644
index 00000000..9058fd12
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/check_in_page.dart
@@ -0,0 +1,270 @@
+import 'package:comwell_key_app/.generated/assets/assets.gen.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_cubit.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/common/components/comwell_card_component.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/utils/l10n_utils.dart';
+import 'package:comwell_key_app/utils/lottie_utils.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';
+
+class CheckInPage extends StatefulWidget {
+ const CheckInPage({super.key});
+
+ @override
+ State<CheckInPage> createState() => _CheckInPageState();
+}
+
+class _CheckInPageState extends State<CheckInPage> with SingleTickerProviderStateMixin {
+ LottieComposition? loadingComposition;
+ late final AnimationController animationController;
+ late final Widget loadingAnimation;
+
+ @override
+ void initState() {
+ animationController = AnimationController(vsync: this);
+ loadingAnimation = Lottie.asset(
+ Assets.animations.loadAnimation,
+ controller: animationController,
+ onLoaded: (composition) {
+ if (loadingComposition == null) {
+ loadingComposition = composition;
+ animationController.duration = composition.duration;
+ }
+ },
+ fit: BoxFit.cover,
+ width: 64,
+ height: 64,
+ );
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ animationController.dispose();
+ super.dispose();
+ }
+
+ void onAnimationEnd() {
+ playLoading();
+ }
+
+ void playLoading() {
+ loadingComposition?.playBetween(
+ animationController,
+ "spinner",
+ markerEnd: "success",
+ repeat: true,
+ );
+ }
+
+ void playError() {
+ loadingComposition?.playBetween(animationController, "error");
+ }
+
+ void playSuccess() {
+ loadingComposition?.playBetween(animationController, "success", markerEnd: "error");
+ }
+
+ double getCardPosition(BuildContext context) {
+ final mq = MediaQuery.of(context);
+ final cubit = context.read<CheckInCubit>();
+ final state = cubit.state;
+ final padding = mq.viewPadding;
+ final height = mq.size.height;
+ final startPosition = height - padding.bottom - padding.top - 100;
+ switch (state.cardState) {
+ case CheckInStatus.loading:
+ case CheckInStatus.error:
+ return height / 2 - 100;
+ case CheckInStatus.done:
+ return 100;
+ case _:
+ return startPosition;
+ }
+ }
+
+ Widget getCardContent(BuildContext context) {
+ final cubit = context.read<CheckInCubit>();
+ if (cubit.state.cardState == CheckInStatus.done) {
+ return Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.all(Radius.circular(30)),
+ border: Border.all(color: colorBackground, width: 2),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text(
+ "${context.strings.check_in_your_digital_card_room_prefix} ${(cubit.booking.roomNumber)}",
+ style: Theme.of(context).textTheme.headlineLarge?.copyWith(
+ color: colorBackground,
+ ),
+ ),
+ ),
+ );
+ } else {
+ return loadingAnimation;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final cubit = context.read<CheckInCubit>();
+ final theme = Theme.of(context);
+
+ return BlocConsumer<CheckInCubit, CheckInState>(
+ listener: (context, state) {
+ switch (state.cardState) {
+ case CheckInStatus.loading:
+ playLoading();
+ break;
+ case CheckInStatus.roomFound:
+ playSuccess();
+ break;
+ case CheckInStatus.error:
+ playError();
+ break;
+ default:
+ break;
+ }
+ },
+ builder: (context, state) {
+ return Scaffold(
+ body: SafeArea(
+ child: Builder(
+ builder: (context) {
+ final position = getCardPosition(context);
+ return Stack(
+ fit: StackFit.expand,
+ children: [
+ AnimatedPositioned(
+ duration: const Duration(milliseconds: 200),
+ top: position,
+ left: 16,
+ right: 16,
+ curve: Easing.linear,
+ onEnd: onAnimationEnd,
+ child: ComwellCard(content: getCardContent(context)),
+ ),
+ if (state.cardState == CheckInStatus.error)
+ Align(
+ alignment: Alignment.bottomCenter,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const SizedBox(height: 15),
+ Text(
+ context.strings.check_in_page_error_title,
+ textAlign: TextAlign.center,
+ style: theme.textTheme.headlineMedium?.copyWith(
+ color: colorHeadlineText,
+ ),
+ ),
+ const SizedBox(height: 15),
+ Text(
+ context.strings.check_in_page_error_subtitle,
+ textAlign: TextAlign.center,
+ style: theme.textTheme.bodySmall?.copyWith(color: colorHeadlineText),
+ ),
+ const SizedBox(height: 15),
+ Text(state.subtitleStringId(context), style: theme.textTheme.bodySmall),
+ const SizedBox(height: 15),
+ const Divider(
+ color: colorDivider,
+ height: 0,
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: ElevatedButton(
+ onPressed: () {
+ context.pop();
+ },
+ style: ButtonStyle(
+ backgroundColor: WidgetStatePropertyAll(sandColor[80]),
+ foregroundColor: const WidgetStatePropertyAll(
+ colorBackground,
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 16.0),
+ child: Text(context.strings.generic_continue),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ if (state.cardState == CheckInStatus.done)
+ Align(
+ alignment: Alignment.bottomCenter,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Text(
+ context.strings.check_in_your_digital_card_nb,
+ textAlign: TextAlign.center,
+ style: theme.textTheme.bodySmall?.copyWith(
+ color: colorHeadlineText,
+ ),
+ ),
+ ),
+ const SizedBox(height: 15),
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Text(
+ state.subtitleStringId(context),
+ style: theme.textTheme.bodySmall,
+ ),
+ ),
+ const SizedBox(height: 15),
+ const Divider(
+ color: colorDivider,
+ height: 0,
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: ElevatedButton(
+ onPressed: () {
+ context.pop(true);
+ },
+ style: ButtonStyle(
+ backgroundColor: WidgetStatePropertyAll(sandColor[80]),
+ foregroundColor: const WidgetStatePropertyAll(
+ colorBackground,
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 16.0),
+ child: Text(context.strings.generic_done),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/check_in_route.dart b/comwell_key_app/lib/presentation/screens/check_in/check_in_route.dart
new file mode 100644
index 00000000..4fc778ef
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/check_in_route.dart
@@ -0,0 +1,42 @@
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_cubit.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/check_in_flow.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/check_in_page.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/routing/app_routes.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:go_router/go_router.dart';
+
+final checkInRoute = GoRoute(
+ path: AppRoutes.checkIn,
+ 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(),
+ );
+ },
+);
+
+final checkInPaymentRoute = GoRoute(
+ path: AppRoutes.checkInPayment,
+ builder: (context, state) {
+ final booking = state.extra as Booking;
+ return BlocProvider(
+ create: (context) => CheckInCubit.withPayment(booking, context.read()),
+ child: BlocBuilder<CheckInCubit, CheckInState>(
+ builder: (context, checkInState) {
+ return CheckInFlow(key: ValueKey(checkInState));
+ },
+ ),
+ );
+ },
+);
diff --git a/comwell_key_app/lib/presentation/screens/check_in/components/check_in_bottom_sheet.dart b/comwell_key_app/lib/presentation/screens/check_in/components/check_in_bottom_sheet.dart
new file mode 100644
index 00000000..054d0d13
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/components/check_in_bottom_sheet.dart
@@ -0,0 +1,63 @@
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_cubit.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/utils/l10n_utils.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class CheckInBottomSheet extends StatelessWidget {
+ final CheckInState state;
+ const CheckInBottomSheet({super.key, required this.state});
+
+ @override
+ Widget build(BuildContext context) {
+ final cubit = context.read<CheckInCubit>();
+ final theme = Theme.of(context);
+ final isEnabled = state.isTermsAccepted;
+
+ final buttonStyle = ButtonStyle(
+ backgroundColor: WidgetStateProperty.resolveWith((states) {
+ if (states.contains(WidgetState.disabled)) {
+ return sandColor.withValues(alpha: 0.5);
+ }
+ return sandColor;
+ }),
+ foregroundColor: const WidgetStatePropertyAll(colorBackground),
+ );
+
+ return Container(
+ decoration: const BoxDecoration(
+ shape: BoxShape.rectangle, color: colorBackground),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Divider(color: colorDivider),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 18.0, right: 18.0, bottom: 32.0, top: 18.0),
+ child: Row(
+ children: [
+ Expanded(
+ child: ElevatedButton(
+ onPressed: isEnabled
+ ? () => cubit.onContinueClicked(context)
+ : null,
+ style: buttonStyle,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 17.0),
+ child: Text(
+ context.strings.checkout_page_payment_payment_title,
+ style: theme.textTheme.bodyMedium
+ ?.copyWith(color: colorBackground),
+ ),
+ )),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 8)
+ ],
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_page_enum.dart b/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_page_enum.dart
new file mode 100644
index 00000000..0d51cbc2
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_page_enum.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/cupertino.dart';
+
+import 'check_in_payment_page.dart';
+
+enum CheckInFlowPage {
+ payment;
+
+ static CheckInFlowPage fromIndex(int index) {
+ return CheckInFlowPage.values[index];
+ }
+
+ static Iterable<Widget> getPages(Key key) {
+ return CheckInFlowPage.values.map((page) {
+ switch (page) {
+ case CheckInFlowPage.payment:
+ return CheckInPaymentPage(key: key);
+ }
+ });
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_payment_page.dart b/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_payment_page.dart
new file mode 100644
index 00000000..a7b82896
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_payment_page.dart
@@ -0,0 +1,38 @@
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_cubit.dart';
+import 'package:comwell_key_app/common/template_pages/payment_page_template.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class CheckInPaymentPage extends StatelessWidget {
+ const CheckInPaymentPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final cubit = context.read<CheckInCubit>();
+ final addOnItems = cubit.state.items;
+ final applyClubPoints = cubit.state.applyClubPoints;
+ final totalPriceBeforeDiscount = cubit.state.totalPriceBeforeDiscount;
+ final totalPriceAfterDiscount = cubit.state.totalPriceAfterDiscount;
+ final trimmedBalance = cubit.trimmedBalance;
+ final clubPoints = cubit.state.clubPoints;
+ final isTermsAccepted = cubit.state.isTermsAccepted;
+ final onAcceptTermsChanged = cubit.onAcceptTermsChanged;
+ final showError = cubit.state.showTermsError;
+ final onShowTermsAndConditions = cubit.showTermsAndConditions;
+ final onApplyClubPointsChanged = cubit.onApplyClubPointsClicked;
+
+ return PaymentPageTemplate(
+ addOnItems: addOnItems,
+ applyClubPoints: applyClubPoints,
+ totalPriceBeforeDiscount: totalPriceBeforeDiscount,
+ totalPriceAfterDiscount: totalPriceAfterDiscount,
+ trimmedBalance: trimmedBalance,
+ clubPoints: clubPoints,
+ isTermsAccepted: isTermsAccepted,
+ userEmail: cubit.booking.bookerLastName,
+ onAcceptTermsChanged: onAcceptTermsChanged,
+ showError: showError,
+ onShowTermsAndConditions: onShowTermsAndConditions,
+ onApplyClubPointsChanged: onApplyClubPointsChanged);
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_processing_page.dart b/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_processing_page.dart
new file mode 100644
index 00000000..ad78c796
--- /dev/null
+++ b/comwell_key_app/lib/presentation/screens/check_in/pages/check_in_processing_page.dart
@@ -0,0 +1,102 @@
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_cubit.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/themes/dark_theme.dart';
+import 'package:comwell_key_app/utils/lottie_utils.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:lottie/lottie.dart';
+
+class CheckInProcessingPage extends StatefulWidget {
+ const CheckInProcessingPage({super.key});
+
+ @override
+ State<CheckInProcessingPage> createState() => _CheckInProcessingPageState();
+}
+
+class _CheckInProcessingPageState extends State<CheckInProcessingPage>
+ with SingleTickerProviderStateMixin {
+ LottieComposition? loadingComposition;
+ late final AnimationController animationController;
+
+ @override
+ void initState() {
+ animationController = AnimationController(vsync: this);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ animationController.dispose();
+ super.dispose();
+ }
+
+ void onAnimationEnd() {
+ playLoading();
+ }
+
+ void playLoading() {
+ loadingComposition?.playBetween(
+ animationController,
+ "spinner",
+ markerEnd: "success",
+ repeat: true,
+ );
+ }
+
+ void playError() async {
+ loadingComposition?.playBetween(
+ animationController,
+ "error",
+ repeat: true,
+ );
+ await Future<void>.delayed(const Duration(seconds: 1));
+ if (mounted) Navigator.of(context).pop();
+ }
+
+ void playSuccess() async {
+ await loadingComposition?.playBetween(
+ animationController,
+ "success",
+ markerEnd: "error",
+ );
+ await Future<void>.delayed(const Duration(seconds: 1));
+ if (mounted) Navigator.of(context).pop();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocListener<CheckInCubit, CheckInState>(
+ listenWhen: (previous, current) =>
+ previous.paymentStatus != current.paymentStatus,
+ listener: (context, state) {
+ if (state.paymentStatus == CheckInPaymentStatus.success) {
+ playSuccess();
+ } else if (state.paymentStatus == CheckInPaymentStatus.error) {
+ playError();
+ }
+ },
+ child: Scaffold(
+ body: Container(
+ alignment: Alignment.center,
+ color: sandColor[80],
+ child: Builder(builder: (context) {
+ return Lottie.asset(
+ 'assets/animations/load_animation.json',
+ controller: animationController,
+ onLoaded: (composition) {
+ if (loadingComposition == null) {
+ loadingComposition = composition;
+ animationController.duration = composition.duration;
+ playLoading();
+ }
+ },
+ fit: BoxFit.cover,
+ width: 64,
+ height: 64,
+ );
+ }),
+ ),
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/presentation/screens/pregistration/components/prereg_bottom_button.dart b/comwell_key_app/lib/presentation/screens/pregistration/components/prereg_bottom_button.dart
index 2a402863..1df43ad1 100644
--- a/comwell_key_app/lib/presentation/screens/pregistration/components/prereg_bottom_button.dart
+++ b/comwell_key_app/lib/presentation/screens/pregistration/components/prereg_bottom_button.dart
@@ -21,8 +21,9 @@ class PreregBottomButton extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 40.0),
child: ElevatedButton(
- onPressed:
- () => cubit.onContinueClicked(context),
+ onPressed: cubit.canContinue
+ ? () => cubit.onContinueClicked(context)
+ : null,
style: ElevatedButton.styleFrom(
backgroundColor: sandColor,
foregroundColor: Colors.white),
diff --git a/comwell_key_app/lib/presentation/screens/pregistration/cubit/preregistration_cubit.dart b/comwell_key_app/lib/presentation/screens/pregistration/cubit/preregistration_cubit.dart
index 15773596..ad5f2a69 100644
--- a/comwell_key_app/lib/presentation/screens/pregistration/cubit/preregistration_cubit.dart
+++ b/comwell_key_app/lib/presentation/screens/pregistration/cubit/preregistration_cubit.dart
@@ -342,7 +342,7 @@ class PreregistrationCubit extends BaseCubit<PreregistrationState> {
duration: const Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
);
- emit(state.copyWith(forceUpdate: true));
+ emit(state.copyWith(forceUpdate: !state.forceUpdate));
_isAnimating = false;
}
@@ -353,7 +353,7 @@ class PreregistrationCubit extends BaseCubit<PreregistrationState> {
duration: const Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
);
- emit(state.copyWith(forceUpdate: true));
+ emit(state.copyWith(forceUpdate: !state.forceUpdate));
_isAnimating = false;
}
@@ -364,7 +364,7 @@ class PreregistrationCubit extends BaseCubit<PreregistrationState> {
duration: const Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
);
- emit(state.copyWith(forceUpdate: true));
+ emit(state.copyWith(forceUpdate: !state.forceUpdate));
_isAnimating = false;
}
diff --git a/comwell_key_app/lib/routing/app_router.dart b/comwell_key_app/lib/routing/app_router.dart
index 2fd4aaa0..083ced32 100644
--- a/comwell_key_app/lib/routing/app_router.dart
+++ b/comwell_key_app/lib/routing/app_router.dart
@@ -1,5 +1,5 @@
import 'package:comwell_key_app/authentication/authentication_repository.dart';
-import 'package:comwell_key_app/check_in/check_in_route.dart';
+import 'package:comwell_key_app/presentation/screens/check_in/check_in_route.dart';
import 'package:comwell_key_app/check_out/checkout_routes.dart';
import 'package:comwell_key_app/choose_share_room/choose_share_room_route.dart';
import 'package:comwell_key_app/hotel_information/hotel_information_route.dart';