From 6b42cd86ff0710fad0355eed568111f22760d666 Mon Sep 17 00:00:00 2001 From: efrilm Date: Fri, 31 Oct 2025 19:24:15 +0700 Subject: [PATCH] split bill --- .../split_bill_form/split_bill_form_bloc.dart | 85 + .../split_bill_form_bloc.freezed.dart | 1506 +++++++++++++++++ .../split_bill_form_event.dart | 17 + .../split_bill_form_state.dart | 23 + lib/common/extension/extension.dart | 1 + lib/common/extension/string_extension.dart | 15 +- lib/common/types/split_type.dart | 12 + lib/injection.config.dart | 3 + .../order/widgets/order_right_panel.dart | 4 +- .../pages/split_bill/split_bill_page.dart | 42 + .../widgets/split_bill_left_panel.dart | 229 +++ .../widgets/split_bill_right_panel.dart | 547 ++++++ lib/presentation/router/app_router.dart | 3 + lib/presentation/router/app_router.gr.dart | 235 +-- 14 files changed, 2622 insertions(+), 100 deletions(-) create mode 100644 lib/application/split_bill/split_bill_form/split_bill_form_bloc.dart create mode 100644 lib/application/split_bill/split_bill_form/split_bill_form_bloc.freezed.dart create mode 100644 lib/application/split_bill/split_bill_form/split_bill_form_event.dart create mode 100644 lib/application/split_bill/split_bill_form/split_bill_form_state.dart create mode 100644 lib/common/types/split_type.dart create mode 100644 lib/presentation/pages/split_bill/split_bill_page.dart create mode 100644 lib/presentation/pages/split_bill/widgets/split_bill_left_panel.dart create mode 100644 lib/presentation/pages/split_bill/widgets/split_bill_right_panel.dart diff --git a/lib/application/split_bill/split_bill_form/split_bill_form_bloc.dart b/lib/application/split_bill/split_bill_form/split_bill_form_bloc.dart new file mode 100644 index 0000000..ef768e7 --- /dev/null +++ b/lib/application/split_bill/split_bill_form/split_bill_form_bloc.dart @@ -0,0 +1,85 @@ +import 'package:bloc/bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:injectable/injectable.dart' hide Order; + +import '../../../common/extension/extension.dart'; +import '../../../common/types/split_type.dart'; +import '../../../domain/customer/customer.dart'; +import '../../../domain/order/order.dart'; + +part 'split_bill_form_event.dart'; +part 'split_bill_form_state.dart'; +part 'split_bill_form_bloc.freezed.dart'; + +@injectable +class SplitBillFormBloc extends Bloc { + SplitBillFormBloc() : super(SplitBillFormState.initial()) { + on(_onSplitBillFormEvent); + } + + Future _onSplitBillFormEvent( + SplitBillFormEvent event, + Emitter emit, + ) { + return event.map( + setOrder: (e) async { + List pendingItems = e.order.orderItems + .where((item) => item.status == 'pending') + .toList(); + + emit( + state.copyWith( + order: e.order, + pendingItems: pendingItems, + splitType: e.order.splitType.isEmpty + ? SplitType.amount + : e.order.splitType.toSplitType(), + ), + ); + }, + customerChanged: (e) async { + emit(state.copyWith(customer: e.customer)); + }, + customerNameChanged: (e) async { + emit(state.copyWith(customerName: e.customerName)); + }, + splitTypeChanged: (e) async { + emit( + state.copyWith( + splitType: e.splitType, + totalAmount: 0, + selectedProducts: {}, + ), + ); + }, + itemQuantityChanged: (e) async { + final newQuantities = Map.from(state.selectedProducts); + + if (e.quantity > 0) { + newQuantities[e.itemId] = e.quantity; + } else { + newQuantities.remove(e.itemId); + } + + // Recalculate total price + int newTotal = 0; + for (var entry in newQuantities.entries) { + final item = state.pendingItems.firstWhere( + (item) => item.id == entry.key, + ); + newTotal += item.unitPrice.toInt() * entry.value; + } + + emit( + state.copyWith( + selectedProducts: newQuantities, + totalAmount: newTotal, + ), + ); + }, + amountChanged: (e) async { + emit(state.copyWith(totalAmount: e.amount)); + }, + ); + } +} diff --git a/lib/application/split_bill/split_bill_form/split_bill_form_bloc.freezed.dart b/lib/application/split_bill/split_bill_form/split_bill_form_bloc.freezed.dart new file mode 100644 index 0000000..82f52f4 --- /dev/null +++ b/lib/application/split_bill/split_bill_form/split_bill_form_bloc.freezed.dart @@ -0,0 +1,1506 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// 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 'split_bill_form_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +/// @nodoc +mixin _$SplitBillFormEvent { + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SplitBillFormEventCopyWith<$Res> { + factory $SplitBillFormEventCopyWith( + SplitBillFormEvent value, + $Res Function(SplitBillFormEvent) then, + ) = _$SplitBillFormEventCopyWithImpl<$Res, SplitBillFormEvent>; +} + +/// @nodoc +class _$SplitBillFormEventCopyWithImpl<$Res, $Val extends SplitBillFormEvent> + implements $SplitBillFormEventCopyWith<$Res> { + _$SplitBillFormEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$SetOrderImplCopyWith<$Res> { + factory _$$SetOrderImplCopyWith( + _$SetOrderImpl value, + $Res Function(_$SetOrderImpl) then, + ) = __$$SetOrderImplCopyWithImpl<$Res>; + @useResult + $Res call({Order order}); + + $OrderCopyWith<$Res> get order; +} + +/// @nodoc +class __$$SetOrderImplCopyWithImpl<$Res> + extends _$SplitBillFormEventCopyWithImpl<$Res, _$SetOrderImpl> + implements _$$SetOrderImplCopyWith<$Res> { + __$$SetOrderImplCopyWithImpl( + _$SetOrderImpl _value, + $Res Function(_$SetOrderImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? order = null}) { + return _then( + _$SetOrderImpl( + null == order + ? _value.order + : order // ignore: cast_nullable_to_non_nullable + as Order, + ), + ); + } + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $OrderCopyWith<$Res> get order { + return $OrderCopyWith<$Res>(_value.order, (value) { + return _then(_value.copyWith(order: value)); + }); + } +} + +/// @nodoc + +class _$SetOrderImpl implements _SetOrder { + const _$SetOrderImpl(this.order); + + @override + final Order order; + + @override + String toString() { + return 'SplitBillFormEvent.setOrder(order: $order)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SetOrderImpl && + (identical(other.order, order) || other.order == order)); + } + + @override + int get hashCode => Object.hash(runtimeType, order); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SetOrderImplCopyWith<_$SetOrderImpl> get copyWith => + __$$SetOrderImplCopyWithImpl<_$SetOrderImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) { + return setOrder(order); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) { + return setOrder?.call(order); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) { + if (setOrder != null) { + return setOrder(order); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) { + return setOrder(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) { + return setOrder?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) { + if (setOrder != null) { + return setOrder(this); + } + return orElse(); + } +} + +abstract class _SetOrder implements SplitBillFormEvent { + const factory _SetOrder(final Order order) = _$SetOrderImpl; + + Order get order; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SetOrderImplCopyWith<_$SetOrderImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$CustomerChangedImplCopyWith<$Res> { + factory _$$CustomerChangedImplCopyWith( + _$CustomerChangedImpl value, + $Res Function(_$CustomerChangedImpl) then, + ) = __$$CustomerChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({Customer customer}); + + $CustomerCopyWith<$Res> get customer; +} + +/// @nodoc +class __$$CustomerChangedImplCopyWithImpl<$Res> + extends _$SplitBillFormEventCopyWithImpl<$Res, _$CustomerChangedImpl> + implements _$$CustomerChangedImplCopyWith<$Res> { + __$$CustomerChangedImplCopyWithImpl( + _$CustomerChangedImpl _value, + $Res Function(_$CustomerChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? customer = null}) { + return _then( + _$CustomerChangedImpl( + null == customer + ? _value.customer + : customer // ignore: cast_nullable_to_non_nullable + as Customer, + ), + ); + } + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $CustomerCopyWith<$Res> get customer { + return $CustomerCopyWith<$Res>(_value.customer, (value) { + return _then(_value.copyWith(customer: value)); + }); + } +} + +/// @nodoc + +class _$CustomerChangedImpl implements _CustomerChanged { + const _$CustomerChangedImpl(this.customer); + + @override + final Customer customer; + + @override + String toString() { + return 'SplitBillFormEvent.customerChanged(customer: $customer)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CustomerChangedImpl && + (identical(other.customer, customer) || + other.customer == customer)); + } + + @override + int get hashCode => Object.hash(runtimeType, customer); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$CustomerChangedImplCopyWith<_$CustomerChangedImpl> get copyWith => + __$$CustomerChangedImplCopyWithImpl<_$CustomerChangedImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) { + return customerChanged(customer); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) { + return customerChanged?.call(customer); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) { + if (customerChanged != null) { + return customerChanged(customer); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) { + return customerChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) { + return customerChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) { + if (customerChanged != null) { + return customerChanged(this); + } + return orElse(); + } +} + +abstract class _CustomerChanged implements SplitBillFormEvent { + const factory _CustomerChanged(final Customer customer) = + _$CustomerChangedImpl; + + Customer get customer; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$CustomerChangedImplCopyWith<_$CustomerChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$CustomerNameChangedImplCopyWith<$Res> { + factory _$$CustomerNameChangedImplCopyWith( + _$CustomerNameChangedImpl value, + $Res Function(_$CustomerNameChangedImpl) then, + ) = __$$CustomerNameChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({String customerName}); +} + +/// @nodoc +class __$$CustomerNameChangedImplCopyWithImpl<$Res> + extends _$SplitBillFormEventCopyWithImpl<$Res, _$CustomerNameChangedImpl> + implements _$$CustomerNameChangedImplCopyWith<$Res> { + __$$CustomerNameChangedImplCopyWithImpl( + _$CustomerNameChangedImpl _value, + $Res Function(_$CustomerNameChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? customerName = null}) { + return _then( + _$CustomerNameChangedImpl( + null == customerName + ? _value.customerName + : customerName // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$CustomerNameChangedImpl implements _CustomerNameChanged { + const _$CustomerNameChangedImpl(this.customerName); + + @override + final String customerName; + + @override + String toString() { + return 'SplitBillFormEvent.customerNameChanged(customerName: $customerName)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CustomerNameChangedImpl && + (identical(other.customerName, customerName) || + other.customerName == customerName)); + } + + @override + int get hashCode => Object.hash(runtimeType, customerName); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$CustomerNameChangedImplCopyWith<_$CustomerNameChangedImpl> get copyWith => + __$$CustomerNameChangedImplCopyWithImpl<_$CustomerNameChangedImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) { + return customerNameChanged(customerName); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) { + return customerNameChanged?.call(customerName); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) { + if (customerNameChanged != null) { + return customerNameChanged(customerName); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) { + return customerNameChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) { + return customerNameChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) { + if (customerNameChanged != null) { + return customerNameChanged(this); + } + return orElse(); + } +} + +abstract class _CustomerNameChanged implements SplitBillFormEvent { + const factory _CustomerNameChanged(final String customerName) = + _$CustomerNameChangedImpl; + + String get customerName; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$CustomerNameChangedImplCopyWith<_$CustomerNameChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$SplitTypeChangedImplCopyWith<$Res> { + factory _$$SplitTypeChangedImplCopyWith( + _$SplitTypeChangedImpl value, + $Res Function(_$SplitTypeChangedImpl) then, + ) = __$$SplitTypeChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({SplitType splitType}); +} + +/// @nodoc +class __$$SplitTypeChangedImplCopyWithImpl<$Res> + extends _$SplitBillFormEventCopyWithImpl<$Res, _$SplitTypeChangedImpl> + implements _$$SplitTypeChangedImplCopyWith<$Res> { + __$$SplitTypeChangedImplCopyWithImpl( + _$SplitTypeChangedImpl _value, + $Res Function(_$SplitTypeChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? splitType = null}) { + return _then( + _$SplitTypeChangedImpl( + null == splitType + ? _value.splitType + : splitType // ignore: cast_nullable_to_non_nullable + as SplitType, + ), + ); + } +} + +/// @nodoc + +class _$SplitTypeChangedImpl implements _SplitTypeChanged { + const _$SplitTypeChangedImpl(this.splitType); + + @override + final SplitType splitType; + + @override + String toString() { + return 'SplitBillFormEvent.splitTypeChanged(splitType: $splitType)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SplitTypeChangedImpl && + (identical(other.splitType, splitType) || + other.splitType == splitType)); + } + + @override + int get hashCode => Object.hash(runtimeType, splitType); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SplitTypeChangedImplCopyWith<_$SplitTypeChangedImpl> get copyWith => + __$$SplitTypeChangedImplCopyWithImpl<_$SplitTypeChangedImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) { + return splitTypeChanged(splitType); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) { + return splitTypeChanged?.call(splitType); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) { + if (splitTypeChanged != null) { + return splitTypeChanged(splitType); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) { + return splitTypeChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) { + return splitTypeChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) { + if (splitTypeChanged != null) { + return splitTypeChanged(this); + } + return orElse(); + } +} + +abstract class _SplitTypeChanged implements SplitBillFormEvent { + const factory _SplitTypeChanged(final SplitType splitType) = + _$SplitTypeChangedImpl; + + SplitType get splitType; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SplitTypeChangedImplCopyWith<_$SplitTypeChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$ItemQuantityChangedImplCopyWith<$Res> { + factory _$$ItemQuantityChangedImplCopyWith( + _$ItemQuantityChangedImpl value, + $Res Function(_$ItemQuantityChangedImpl) then, + ) = __$$ItemQuantityChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({String itemId, int quantity}); +} + +/// @nodoc +class __$$ItemQuantityChangedImplCopyWithImpl<$Res> + extends _$SplitBillFormEventCopyWithImpl<$Res, _$ItemQuantityChangedImpl> + implements _$$ItemQuantityChangedImplCopyWith<$Res> { + __$$ItemQuantityChangedImplCopyWithImpl( + _$ItemQuantityChangedImpl _value, + $Res Function(_$ItemQuantityChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? itemId = null, Object? quantity = null}) { + return _then( + _$ItemQuantityChangedImpl( + itemId: null == itemId + ? _value.itemId + : itemId // ignore: cast_nullable_to_non_nullable + as String, + quantity: null == quantity + ? _value.quantity + : quantity // ignore: cast_nullable_to_non_nullable + as int, + ), + ); + } +} + +/// @nodoc + +class _$ItemQuantityChangedImpl implements _ItemQuantityChanged { + const _$ItemQuantityChangedImpl({ + required this.itemId, + required this.quantity, + }); + + @override + final String itemId; + @override + final int quantity; + + @override + String toString() { + return 'SplitBillFormEvent.itemQuantityChanged(itemId: $itemId, quantity: $quantity)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ItemQuantityChangedImpl && + (identical(other.itemId, itemId) || other.itemId == itemId) && + (identical(other.quantity, quantity) || + other.quantity == quantity)); + } + + @override + int get hashCode => Object.hash(runtimeType, itemId, quantity); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ItemQuantityChangedImplCopyWith<_$ItemQuantityChangedImpl> get copyWith => + __$$ItemQuantityChangedImplCopyWithImpl<_$ItemQuantityChangedImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) { + return itemQuantityChanged(itemId, quantity); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) { + return itemQuantityChanged?.call(itemId, quantity); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) { + if (itemQuantityChanged != null) { + return itemQuantityChanged(itemId, quantity); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) { + return itemQuantityChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) { + return itemQuantityChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) { + if (itemQuantityChanged != null) { + return itemQuantityChanged(this); + } + return orElse(); + } +} + +abstract class _ItemQuantityChanged implements SplitBillFormEvent { + const factory _ItemQuantityChanged({ + required final String itemId, + required final int quantity, + }) = _$ItemQuantityChangedImpl; + + String get itemId; + int get quantity; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ItemQuantityChangedImplCopyWith<_$ItemQuantityChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$AmountChangedImplCopyWith<$Res> { + factory _$$AmountChangedImplCopyWith( + _$AmountChangedImpl value, + $Res Function(_$AmountChangedImpl) then, + ) = __$$AmountChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({int amount}); +} + +/// @nodoc +class __$$AmountChangedImplCopyWithImpl<$Res> + extends _$SplitBillFormEventCopyWithImpl<$Res, _$AmountChangedImpl> + implements _$$AmountChangedImplCopyWith<$Res> { + __$$AmountChangedImplCopyWithImpl( + _$AmountChangedImpl _value, + $Res Function(_$AmountChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? amount = null}) { + return _then( + _$AmountChangedImpl( + null == amount + ? _value.amount + : amount // ignore: cast_nullable_to_non_nullable + as int, + ), + ); + } +} + +/// @nodoc + +class _$AmountChangedImpl implements _AmountChanged { + const _$AmountChangedImpl(this.amount); + + @override + final int amount; + + @override + String toString() { + return 'SplitBillFormEvent.amountChanged(amount: $amount)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AmountChangedImpl && + (identical(other.amount, amount) || other.amount == amount)); + } + + @override + int get hashCode => Object.hash(runtimeType, amount); + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$AmountChangedImplCopyWith<_$AmountChangedImpl> get copyWith => + __$$AmountChangedImplCopyWithImpl<_$AmountChangedImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Order order) setOrder, + required TResult Function(Customer customer) customerChanged, + required TResult Function(String customerName) customerNameChanged, + required TResult Function(SplitType splitType) splitTypeChanged, + required TResult Function(String itemId, int quantity) itemQuantityChanged, + required TResult Function(int amount) amountChanged, + }) { + return amountChanged(amount); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Order order)? setOrder, + TResult? Function(Customer customer)? customerChanged, + TResult? Function(String customerName)? customerNameChanged, + TResult? Function(SplitType splitType)? splitTypeChanged, + TResult? Function(String itemId, int quantity)? itemQuantityChanged, + TResult? Function(int amount)? amountChanged, + }) { + return amountChanged?.call(amount); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Order order)? setOrder, + TResult Function(Customer customer)? customerChanged, + TResult Function(String customerName)? customerNameChanged, + TResult Function(SplitType splitType)? splitTypeChanged, + TResult Function(String itemId, int quantity)? itemQuantityChanged, + TResult Function(int amount)? amountChanged, + required TResult orElse(), + }) { + if (amountChanged != null) { + return amountChanged(amount); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_SetOrder value) setOrder, + required TResult Function(_CustomerChanged value) customerChanged, + required TResult Function(_CustomerNameChanged value) customerNameChanged, + required TResult Function(_SplitTypeChanged value) splitTypeChanged, + required TResult Function(_ItemQuantityChanged value) itemQuantityChanged, + required TResult Function(_AmountChanged value) amountChanged, + }) { + return amountChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_SetOrder value)? setOrder, + TResult? Function(_CustomerChanged value)? customerChanged, + TResult? Function(_CustomerNameChanged value)? customerNameChanged, + TResult? Function(_SplitTypeChanged value)? splitTypeChanged, + TResult? Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult? Function(_AmountChanged value)? amountChanged, + }) { + return amountChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_SetOrder value)? setOrder, + TResult Function(_CustomerChanged value)? customerChanged, + TResult Function(_CustomerNameChanged value)? customerNameChanged, + TResult Function(_SplitTypeChanged value)? splitTypeChanged, + TResult Function(_ItemQuantityChanged value)? itemQuantityChanged, + TResult Function(_AmountChanged value)? amountChanged, + required TResult orElse(), + }) { + if (amountChanged != null) { + return amountChanged(this); + } + return orElse(); + } +} + +abstract class _AmountChanged implements SplitBillFormEvent { + const factory _AmountChanged(final int amount) = _$AmountChangedImpl; + + int get amount; + + /// Create a copy of SplitBillFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$AmountChangedImplCopyWith<_$AmountChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +mixin _$SplitBillFormState { + Order get order => throw _privateConstructorUsedError; + List get pendingItems => throw _privateConstructorUsedError; + Customer? get customer => throw _privateConstructorUsedError; + String get customerName => throw _privateConstructorUsedError; + SplitType get splitType => throw _privateConstructorUsedError; + Map get selectedProducts => throw _privateConstructorUsedError; + int get totalAmount => throw _privateConstructorUsedError; + bool get isConfirming => throw _privateConstructorUsedError; + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SplitBillFormStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SplitBillFormStateCopyWith<$Res> { + factory $SplitBillFormStateCopyWith( + SplitBillFormState value, + $Res Function(SplitBillFormState) then, + ) = _$SplitBillFormStateCopyWithImpl<$Res, SplitBillFormState>; + @useResult + $Res call({ + Order order, + List pendingItems, + Customer? customer, + String customerName, + SplitType splitType, + Map selectedProducts, + int totalAmount, + bool isConfirming, + }); + + $OrderCopyWith<$Res> get order; + $CustomerCopyWith<$Res>? get customer; +} + +/// @nodoc +class _$SplitBillFormStateCopyWithImpl<$Res, $Val extends SplitBillFormState> + implements $SplitBillFormStateCopyWith<$Res> { + _$SplitBillFormStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? order = null, + Object? pendingItems = null, + Object? customer = freezed, + Object? customerName = null, + Object? splitType = null, + Object? selectedProducts = null, + Object? totalAmount = null, + Object? isConfirming = null, + }) { + return _then( + _value.copyWith( + order: null == order + ? _value.order + : order // ignore: cast_nullable_to_non_nullable + as Order, + pendingItems: null == pendingItems + ? _value.pendingItems + : pendingItems // ignore: cast_nullable_to_non_nullable + as List, + customer: freezed == customer + ? _value.customer + : customer // ignore: cast_nullable_to_non_nullable + as Customer?, + customerName: null == customerName + ? _value.customerName + : customerName // ignore: cast_nullable_to_non_nullable + as String, + splitType: null == splitType + ? _value.splitType + : splitType // ignore: cast_nullable_to_non_nullable + as SplitType, + selectedProducts: null == selectedProducts + ? _value.selectedProducts + : selectedProducts // ignore: cast_nullable_to_non_nullable + as Map, + totalAmount: null == totalAmount + ? _value.totalAmount + : totalAmount // ignore: cast_nullable_to_non_nullable + as int, + isConfirming: null == isConfirming + ? _value.isConfirming + : isConfirming // ignore: cast_nullable_to_non_nullable + as bool, + ) + as $Val, + ); + } + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $OrderCopyWith<$Res> get order { + return $OrderCopyWith<$Res>(_value.order, (value) { + return _then(_value.copyWith(order: value) as $Val); + }); + } + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $CustomerCopyWith<$Res>? get customer { + if (_value.customer == null) { + return null; + } + + return $CustomerCopyWith<$Res>(_value.customer!, (value) { + return _then(_value.copyWith(customer: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SplitBillFormStateImplCopyWith<$Res> + implements $SplitBillFormStateCopyWith<$Res> { + factory _$$SplitBillFormStateImplCopyWith( + _$SplitBillFormStateImpl value, + $Res Function(_$SplitBillFormStateImpl) then, + ) = __$$SplitBillFormStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + Order order, + List pendingItems, + Customer? customer, + String customerName, + SplitType splitType, + Map selectedProducts, + int totalAmount, + bool isConfirming, + }); + + @override + $OrderCopyWith<$Res> get order; + @override + $CustomerCopyWith<$Res>? get customer; +} + +/// @nodoc +class __$$SplitBillFormStateImplCopyWithImpl<$Res> + extends _$SplitBillFormStateCopyWithImpl<$Res, _$SplitBillFormStateImpl> + implements _$$SplitBillFormStateImplCopyWith<$Res> { + __$$SplitBillFormStateImplCopyWithImpl( + _$SplitBillFormStateImpl _value, + $Res Function(_$SplitBillFormStateImpl) _then, + ) : super(_value, _then); + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? order = null, + Object? pendingItems = null, + Object? customer = freezed, + Object? customerName = null, + Object? splitType = null, + Object? selectedProducts = null, + Object? totalAmount = null, + Object? isConfirming = null, + }) { + return _then( + _$SplitBillFormStateImpl( + order: null == order + ? _value.order + : order // ignore: cast_nullable_to_non_nullable + as Order, + pendingItems: null == pendingItems + ? _value._pendingItems + : pendingItems // ignore: cast_nullable_to_non_nullable + as List, + customer: freezed == customer + ? _value.customer + : customer // ignore: cast_nullable_to_non_nullable + as Customer?, + customerName: null == customerName + ? _value.customerName + : customerName // ignore: cast_nullable_to_non_nullable + as String, + splitType: null == splitType + ? _value.splitType + : splitType // ignore: cast_nullable_to_non_nullable + as SplitType, + selectedProducts: null == selectedProducts + ? _value._selectedProducts + : selectedProducts // ignore: cast_nullable_to_non_nullable + as Map, + totalAmount: null == totalAmount + ? _value.totalAmount + : totalAmount // ignore: cast_nullable_to_non_nullable + as int, + isConfirming: null == isConfirming + ? _value.isConfirming + : isConfirming // ignore: cast_nullable_to_non_nullable + as bool, + ), + ); + } +} + +/// @nodoc + +class _$SplitBillFormStateImpl implements _SplitBillFormState { + _$SplitBillFormStateImpl({ + required this.order, + required final List pendingItems, + this.customer, + required this.customerName, + required this.splitType, + required final Map selectedProducts, + this.totalAmount = 0, + this.isConfirming = false, + }) : _pendingItems = pendingItems, + _selectedProducts = selectedProducts; + + @override + final Order order; + final List _pendingItems; + @override + List get pendingItems { + if (_pendingItems is EqualUnmodifiableListView) return _pendingItems; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_pendingItems); + } + + @override + final Customer? customer; + @override + final String customerName; + @override + final SplitType splitType; + final Map _selectedProducts; + @override + Map get selectedProducts { + if (_selectedProducts is EqualUnmodifiableMapView) return _selectedProducts; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_selectedProducts); + } + + @override + @JsonKey() + final int totalAmount; + @override + @JsonKey() + final bool isConfirming; + + @override + String toString() { + return 'SplitBillFormState(order: $order, pendingItems: $pendingItems, customer: $customer, customerName: $customerName, splitType: $splitType, selectedProducts: $selectedProducts, totalAmount: $totalAmount, isConfirming: $isConfirming)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SplitBillFormStateImpl && + (identical(other.order, order) || other.order == order) && + const DeepCollectionEquality().equals( + other._pendingItems, + _pendingItems, + ) && + (identical(other.customer, customer) || + other.customer == customer) && + (identical(other.customerName, customerName) || + other.customerName == customerName) && + (identical(other.splitType, splitType) || + other.splitType == splitType) && + const DeepCollectionEquality().equals( + other._selectedProducts, + _selectedProducts, + ) && + (identical(other.totalAmount, totalAmount) || + other.totalAmount == totalAmount) && + (identical(other.isConfirming, isConfirming) || + other.isConfirming == isConfirming)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + order, + const DeepCollectionEquality().hash(_pendingItems), + customer, + customerName, + splitType, + const DeepCollectionEquality().hash(_selectedProducts), + totalAmount, + isConfirming, + ); + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SplitBillFormStateImplCopyWith<_$SplitBillFormStateImpl> get copyWith => + __$$SplitBillFormStateImplCopyWithImpl<_$SplitBillFormStateImpl>( + this, + _$identity, + ); +} + +abstract class _SplitBillFormState implements SplitBillFormState { + factory _SplitBillFormState({ + required final Order order, + required final List pendingItems, + final Customer? customer, + required final String customerName, + required final SplitType splitType, + required final Map selectedProducts, + final int totalAmount, + final bool isConfirming, + }) = _$SplitBillFormStateImpl; + + @override + Order get order; + @override + List get pendingItems; + @override + Customer? get customer; + @override + String get customerName; + @override + SplitType get splitType; + @override + Map get selectedProducts; + @override + int get totalAmount; + @override + bool get isConfirming; + + /// Create a copy of SplitBillFormState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SplitBillFormStateImplCopyWith<_$SplitBillFormStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/split_bill/split_bill_form/split_bill_form_event.dart b/lib/application/split_bill/split_bill_form/split_bill_form_event.dart new file mode 100644 index 0000000..892aa93 --- /dev/null +++ b/lib/application/split_bill/split_bill_form/split_bill_form_event.dart @@ -0,0 +1,17 @@ +part of 'split_bill_form_bloc.dart'; + +@freezed +class SplitBillFormEvent with _$SplitBillFormEvent { + const factory SplitBillFormEvent.setOrder(Order order) = _SetOrder; + const factory SplitBillFormEvent.customerChanged(Customer customer) = + _CustomerChanged; + const factory SplitBillFormEvent.customerNameChanged(String customerName) = + _CustomerNameChanged; + const factory SplitBillFormEvent.splitTypeChanged(SplitType splitType) = + _SplitTypeChanged; + const factory SplitBillFormEvent.itemQuantityChanged({ + required String itemId, + required int quantity, + }) = _ItemQuantityChanged; + const factory SplitBillFormEvent.amountChanged(int amount) = _AmountChanged; +} diff --git a/lib/application/split_bill/split_bill_form/split_bill_form_state.dart b/lib/application/split_bill/split_bill_form/split_bill_form_state.dart new file mode 100644 index 0000000..6b7aa81 --- /dev/null +++ b/lib/application/split_bill/split_bill_form/split_bill_form_state.dart @@ -0,0 +1,23 @@ +part of 'split_bill_form_bloc.dart'; + +@freezed +class SplitBillFormState with _$SplitBillFormState { + factory SplitBillFormState({ + required Order order, + required List pendingItems, + Customer? customer, + required String customerName, + required SplitType splitType, + required Map selectedProducts, + @Default(0) int totalAmount, + @Default(false) bool isConfirming, + }) = _SplitBillFormState; + + factory SplitBillFormState.initial() => SplitBillFormState( + order: Order.empty(), + pendingItems: [], + customerName: '', + splitType: SplitType.amount, + selectedProducts: {}, + ); +} diff --git a/lib/common/extension/extension.dart b/lib/common/extension/extension.dart index c699ca8..32db007 100644 --- a/lib/common/extension/extension.dart +++ b/lib/common/extension/extension.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../../domain/table/table.dart'; +import '../types/split_type.dart'; import '../types/void_type.dart'; part 'build_context_extension.dart'; diff --git a/lib/common/extension/string_extension.dart b/lib/common/extension/string_extension.dart index 802c567..0432559 100644 --- a/lib/common/extension/string_extension.dart +++ b/lib/common/extension/string_extension.dart @@ -39,12 +39,23 @@ extension StringX on String { VoidType toVoidType() { switch (this) { - case 'all': + case 'ALL': return VoidType.all; - case 'item': + case 'ITEM': return VoidType.item; default: return VoidType.unknown; } } + + SplitType toSplitType() { + switch (this) { + case 'AMOUNT': + return SplitType.amount; + case 'ITEM': + return SplitType.item; + default: + return SplitType.unknown; + } + } } diff --git a/lib/common/types/split_type.dart b/lib/common/types/split_type.dart new file mode 100644 index 0000000..8533e13 --- /dev/null +++ b/lib/common/types/split_type.dart @@ -0,0 +1,12 @@ +enum SplitType { amount, item, unknown } + +extension SplitTypeX on SplitType { + String toStringType() => switch (this) { + SplitType.amount => 'AMOUNT', + SplitType.item => 'ITEM', + SplitType.unknown => 'unknown', + }; + + bool get isAmount => this == SplitType.amount; + bool get isItem => this == SplitType.item; +} diff --git a/lib/injection.config.dart b/lib/injection.config.dart index a29b471..3a8a749 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -30,6 +30,8 @@ import 'package:apskel_pos_flutter_v2/application/payment_method/payment_method_ as _i952; import 'package:apskel_pos_flutter_v2/application/product/product_loader/product_loader_bloc.dart' as _i13; +import 'package:apskel_pos_flutter_v2/application/split_bill/split_bill_form/split_bill_form_bloc.dart' + as _i334; import 'package:apskel_pos_flutter_v2/application/sync/sync_bloc.dart' as _i741; import 'package:apskel_pos_flutter_v2/application/table/table_form/table_form_bloc.dart' as _i248; @@ -126,6 +128,7 @@ extension GetItInjectableX on _i174.GetIt { preResolve: true, ); gh.factory<_i13.CheckoutFormBloc>(() => _i13.CheckoutFormBloc()); + gh.factory<_i334.SplitBillFormBloc>(() => _i334.SplitBillFormBloc()); gh.singleton<_i487.DatabaseHelper>(() => databaseDi.databaseHelper); gh.lazySingleton<_i361.Dio>(() => dioDi.dio); gh.lazySingleton<_i800.AppRouter>(() => autoRouteDi.appRouter); diff --git a/lib/presentation/pages/order/widgets/order_right_panel.dart b/lib/presentation/pages/order/widgets/order_right_panel.dart index ff5b3d7..a3ffd7b 100644 --- a/lib/presentation/pages/order/widgets/order_right_panel.dart +++ b/lib/presentation/pages/order/widgets/order_right_panel.dart @@ -96,7 +96,9 @@ class OrderRightPanel extends StatelessWidget { SpaceWidth(8), AppElevatedButton.outlined( onPressed: () { - // context.push(SplitBillPage(order: orderDetail!)); + context.router.push( + SplitBillRoute(order: state.selectedOrder!), + ); }, label: 'Split Bill', icon: Icon(Icons.calculate_outlined), diff --git a/lib/presentation/pages/split_bill/split_bill_page.dart b/lib/presentation/pages/split_bill/split_bill_page.dart new file mode 100644 index 0000000..1ab05bb --- /dev/null +++ b/lib/presentation/pages/split_bill/split_bill_page.dart @@ -0,0 +1,42 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../application/split_bill/split_bill_form/split_bill_form_bloc.dart'; +import '../../../common/theme/theme.dart'; +import '../../../domain/order/order.dart'; +import '../../../injection.dart'; +import 'widgets/split_bill_left_panel.dart'; +import 'widgets/split_bill_right_panel.dart'; + +@RoutePage() +class SplitBillPage extends StatelessWidget implements AutoRouteWrapper { + final Order order; + const SplitBillPage({super.key, required this.order}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColor.background, + body: SafeArea( + child: BlocBuilder( + builder: (context, state) { + return Row( + children: [ + Expanded(flex: 2, child: SplitBillLeftPanel(state: state)), + Expanded(flex: 4, child: SplitBillRightPanel(state: state)), + ], + ); + }, + ), + ), + ); + } + + @override + Widget wrappedRoute(BuildContext context) => BlocProvider( + create: (_) => + getIt()..add(SplitBillFormEvent.setOrder(order)), + child: this, + ); +} diff --git a/lib/presentation/pages/split_bill/widgets/split_bill_left_panel.dart b/lib/presentation/pages/split_bill/widgets/split_bill_left_panel.dart new file mode 100644 index 0000000..0616498 --- /dev/null +++ b/lib/presentation/pages/split_bill/widgets/split_bill_left_panel.dart @@ -0,0 +1,229 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../../../../application/split_bill/split_bill_form/split_bill_form_bloc.dart'; +import '../../../../common/extension/extension.dart'; +import '../../../../common/theme/theme.dart'; +import '../../../../domain/order/order.dart'; +import '../../../components/border/dashed_border.dart'; +import '../../../components/page/page_title.dart'; +import '../../../components/spaces/space.dart'; + +class SplitBillLeftPanel extends StatelessWidget { + final SplitBillFormState state; + const SplitBillLeftPanel({super.key, required this.state}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration(color: AppColor.white), + child: Column( + children: [ + PageTitle( + title: 'Ringkasan Pesanan', + subtitle: state.order.orderNumber, + ), + SpaceHeight(16), + Expanded( + child: ListView.builder( + padding: EdgeInsets.symmetric(horizontal: 16), + itemCount: state.pendingItems.length, + itemBuilder: (context, index) { + return _buildOrderItem(state.pendingItems[index]); + }, + ), + ), + DashedDivider(color: AppColor.border), + + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + if (state.order.subtotal != state.order.totalAmount) ...[ + _buildSummaryItem( + title: 'Subtotal', + value: state.order.subtotal.currencyFormatRpV2, + ), + if ((state.order.taxAmount) > 0) + _buildSummaryItem( + title: 'Pajax', + value: state.order.taxAmount.currencyFormatRpV2, + ), + if ((state.order.discountAmount) > 0) + _buildSummaryItem( + title: 'Diskon', + value: state.order.discountAmount.currencyFormatRpV2, + ), + SpaceHeight(8), + ], + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total terbayar', + style: AppStyle.md.copyWith( + color: AppColor.textPrimary, + fontWeight: FontWeight.w600, + ), + ), + Text( + state.order.totalPaid.currencyFormatRpV2, + style: AppStyle.md.copyWith( + color: AppColor.textPrimary, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + SpaceHeight(4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Sisa Tagihan', + style: AppStyle.md.copyWith( + color: AppColor.error, + fontWeight: FontWeight.w600, + ), + ), + Text( + state.order.remainingAmount.currencyFormatRpV2, + style: AppStyle.md.copyWith( + color: AppColor.error, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + SpaceHeight(6), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total Pembayaran', + style: AppStyle.lg.copyWith( + color: AppColor.primary, + fontWeight: FontWeight.bold, + ), + ), + Text( + state.order.totalAmount.currencyFormatRpV2, + style: AppStyle.lg.copyWith( + color: AppColor.primary, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + Row _buildSummaryItem({required String title, required String value}) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title, style: AppStyle.md.copyWith(color: AppColor.textSecondary)), + Text(value, style: AppStyle.md.copyWith(color: AppColor.textSecondary)), + ], + ); + } + + Widget _buildOrderItem(OrderItem item) { + return Container( + padding: EdgeInsets.symmetric(vertical: 12), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.productName, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + if (item.productVariantName.isNotEmpty) + Text( + item.productVariantName, + style: AppStyle.md.copyWith( + color: AppColor.textSecondary, + fontStyle: FontStyle.italic, + ), + ), + Text( + 'Qty: ${item.quantity} x Rp ${item.unitPrice.currencyFormatRpV2} ', + style: AppStyle.md.copyWith( + color: AppColor.textSecondary, + ), + ), + ], + ), + ), + Text( + item.totalPrice.currencyFormatRpV2, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + ], + ), + if ((item.paidQuantity) > 1) ...[ + SpaceHeight(6), + Align( + alignment: Alignment.centerRight, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 4, + ), + decoration: BoxDecoration( + color: AppColor.primary.withOpacity(0.2), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColor.primary), + ), + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${item.paidQuantity} ', + style: AppStyle.sm.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.primary, + ), + ), + TextSpan( + text: 'dari ', + style: AppStyle.sm.copyWith(color: AppColor.primary), + ), + TextSpan( + text: '${item.quantity} ', + style: AppStyle.sm.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.primary, + ), + ), + TextSpan( + text: 'kuantiti telah dibayar.', + style: AppStyle.sm.copyWith(color: AppColor.primary), + ), + ], + ), + ), + ), + ), + ], + ], + ), + ); + } +} diff --git a/lib/presentation/pages/split_bill/widgets/split_bill_right_panel.dart b/lib/presentation/pages/split_bill/widgets/split_bill_right_panel.dart new file mode 100644 index 0000000..069b16b --- /dev/null +++ b/lib/presentation/pages/split_bill/widgets/split_bill_right_panel.dart @@ -0,0 +1,547 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../application/split_bill/split_bill_form/split_bill_form_bloc.dart'; +import '../../../../common/extension/extension.dart'; +import '../../../../common/theme/theme.dart'; +import '../../../../common/types/split_type.dart'; +import '../../../../domain/order/order.dart'; +import '../../../components/button/button.dart'; +import '../../../components/field/field.dart'; +import '../../../components/spaces/space.dart'; +import '../../../components/toast/flushbar.dart'; +import '../../../router/app_router.gr.dart'; + +class SplitBillRightPanel extends StatefulWidget { + final SplitBillFormState state; + const SplitBillRightPanel({super.key, required this.state}); + + @override + State createState() => _SplitBillRightPanelState(); +} + +class _SplitBillRightPanelState extends State { + final TextEditingController _customerController = TextEditingController(); + final TextEditingController _amountController = TextEditingController(); + + @override + void initState() { + super.initState(); + _customerController.addListener(() { + context.read().add( + SplitBillFormEvent.customerNameChanged(_customerController.text), + ); + }); + } + + @override + void dispose() { + super.dispose(); + _customerController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + child: SingleChildScrollView( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Split Bill', + style: AppStyle.h5.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.textPrimary, + ), + ), + SpaceHeight(16), + _buildCustomer(context), + SpaceHeight(16), + _buildRowButtonSplit(), + SpaceHeight(16), + Container( + child: widget.state.splitType.isItem + ? _buildPerProductSplit() + : _buildPerAmountSplit(), + ), + SpaceHeight(16), + ], + ), + ), + ), + _buildBottom(), + ], + ); + } + + Widget _buildBottom() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration(color: AppColor.white), + child: Row( + children: [ + Expanded( + child: AppElevatedButton.outlined( + onPressed: () => context.router.maybePop(), + label: 'Batal', + ), + ), + SpaceWidth(16), + Expanded( + child: AppElevatedButton.filled( + onPressed: () => _confirmSplit(), + label: 'Konfirmasi Split', + ), + ), + ], + ), + ); + } + + Widget _buildPerAmountSplit() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Masukkan Jumlah yang Akan Dibayar', + style: AppStyle.xl.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.textPrimary, + ), + ), + SizedBox(height: 16), + Column( + children: [ + Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColor.white, + border: Border.all(color: AppColor.border), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Split Bill', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.primary, + ), + ), + SizedBox(height: 16), + Text( + 'Jumlah yang akan dibayar:', + style: AppStyle.md.copyWith(color: AppColor.textSecondary), + ), + SizedBox(height: 8), + Container( + decoration: BoxDecoration( + border: Border.all(color: AppColor.border), + borderRadius: BorderRadius.circular(8), + ), + child: TextField( + controller: _amountController, + keyboardType: TextInputType.number, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + onChanged: (value) { + context.read().add( + SplitBillFormEvent.amountChanged( + int.tryParse(value) ?? 0, + ), + ); + // setState(() { + // splitAmount = int.tryParse(value) ?? 0; + // }); + }, + decoration: InputDecoration( + hintText: '0', + prefixText: 'Rp ', + prefixStyle: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + border: InputBorder.none, + contentPadding: EdgeInsets.all(16), + ), + ), + ), + SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total yang dibayar:', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + Text( + widget.state.totalAmount.currencyFormatRpV2, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.primary, + ), + ), + ], + ), + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Sisa bill:', + style: AppStyle.md.copyWith( + color: AppColor.textSecondary, + ), + ), + Text( + (widget.state.order.remainingAmount - + widget.state.totalAmount) + .currencyFormatRpV2, + style: AppStyle.md.copyWith( + color: AppColor.textSecondary, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ], + ); + } + + Widget _buildPerProductSplit() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Pilih Produk untuk Split', + style: AppStyle.xl.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.textPrimary, + ), + ), + SizedBox(height: 16), + Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColor.white, + border: Border.all(color: AppColor.border), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Split Bill', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.primary, + ), + ), + SizedBox(height: 16), + ListView.builder( + itemCount: widget.state.pendingItems.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return _buildProductSplitItem( + widget.state.pendingItems[index], + ); + }, + ), + Container( + width: double.infinity, + height: 1, + color: AppColor.border, + margin: EdgeInsets.symmetric(vertical: 12), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total Split:', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + Text( + widget.state.totalAmount.currencyFormatRpV2, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.primary, + ), + ), + ], + ), + ], + ), + ), + ], + ); + } + + Widget _buildProductSplitItem(OrderItem item) { + int selectedQty = widget.state.selectedProducts[item.id] ?? 0; + int maxQty = (item.quantity) - (item.paidQuantity); + + return Container( + margin: EdgeInsets.only(bottom: 12), + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: selectedQty > 0 + ? AppColor.primary.withOpacity(0.1) + : Colors.transparent, + border: Border.all( + color: selectedQty > 0 ? AppColor.primary : AppColor.border, + ), + borderRadius: BorderRadius.circular(6), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.productName, + style: AppStyle.md.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + if (item.productVariantName.isNotEmpty) + Text( + item.productVariantName, + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + fontStyle: FontStyle.italic, + ), + ), + Text( + '${item.unitPrice.currencyFormatRpV2} per item', + style: AppStyle.sm.copyWith(color: AppColor.textSecondary), + ), + ], + ), + ), + Row( + children: [ + GestureDetector( + onTap: () { + if (selectedQty > 0) { + setState(() { + context.read().add( + SplitBillFormEvent.itemQuantityChanged( + itemId: item.id, + quantity: selectedQty - 1, + ), + ); + }); + } + }, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: selectedQty > 0 ? AppColor.primary : AppColor.border, + borderRadius: BorderRadius.circular(4), + ), + child: Icon( + Icons.remove, + size: 16, + color: selectedQty > 0 + ? Colors.white + : AppColor.textSecondary, + ), + ), + ), + Container( + width: 40, + height: 32, + margin: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + border: Border.all(color: AppColor.border), + borderRadius: BorderRadius.circular(4), + ), + child: Center( + child: Text( + '$selectedQty', + style: AppStyle.md.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.textPrimary, + ), + ), + ), + ), + GestureDetector( + onTap: () { + context.read().add( + SplitBillFormEvent.itemQuantityChanged( + itemId: item.id, + quantity: selectedQty + 1, + ), + ); + }, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: selectedQty < maxQty + ? AppColor.primary + : AppColor.border, + borderRadius: BorderRadius.circular(4), + ), + child: Icon( + Icons.add, + size: 16, + color: selectedQty < maxQty + ? Colors.white + : AppColor.textSecondary, + ), + ), + ), + ], + ), + ], + ), + ); + } + + Widget _buildRowButtonSplit() { + switch (widget.state.order.splitType) { + case 'AMOUNT': + return _buildSplitTypeButton('Per Jumlah', SplitType.amount); + case 'ITEM': + return _buildSplitTypeButton('Per Produk', SplitType.item); + default: + return Row( + children: [ + _buildSplitTypeButton('Per Produk', SplitType.item), + SizedBox(width: 16), + _buildSplitTypeButton('Per Jumlah', SplitType.amount), + ], + ); + } + } + + Widget _buildSplitTypeButton(String title, SplitType type) { + bool isSelected = widget.state.splitType == type; + return GestureDetector( + onTap: () { + _amountController.clear(); + context.read().add( + SplitBillFormEvent.splitTypeChanged(type), + ); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), + decoration: BoxDecoration( + color: isSelected ? AppColor.primary : Colors.transparent, + border: Border.all( + color: isSelected ? AppColor.primary : AppColor.border, + width: 2, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + title, + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: isSelected ? AppColor.white : AppColor.textPrimary, + ), + ), + ), + ); + } + + Widget _buildCustomer(BuildContext context) { + return Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColor.white, + borderRadius: BorderRadius.circular(12), + ), + child: CustomerAutocomplete( + controller: _customerController, + selectedCustomer: widget.state.customer, + onSelected: (customer) { + context.read().add( + SplitBillFormEvent.customerChanged(customer!), + ); + }, + ), + ); + } + + void _confirmSplit() { + if (widget.state.splitType.isItem) { + int splitTotal = widget.state.totalAmount; + if (splitTotal > 0) { + List splitItems = []; + widget.state.selectedProducts.forEach((itemId, quantity) { + OrderItem? originalItem = widget.state.pendingItems.firstWhere( + (item) => item.id == itemId, + orElse: () => OrderItem.empty(), + ); + // ignore: unnecessary_null_comparison + if (originalItem != null && originalItem.id != null) { + splitItems.add( + originalItem.copyWith( + quantity: quantity, + totalPrice: (originalItem.unitPrice) * quantity, + ), + ); + } + }); + + context.router.push( + PaymentRoute( + order: widget.state.order.copyWith( + orderItems: splitItems, + subtotal: splitTotal, + totalAmount: splitTotal, + ), + ), + ); + } else { + AppFlushbar.showError(context, "Pilih minimal satu produk untuk split"); + } + } else { + int totalAmount = + (widget.state.order.totalAmount) - (widget.state.order.totalPaid); + + if (widget.state.totalAmount > 0 && + widget.state.totalAmount <= totalAmount) { + context.router.push( + PaymentRoute( + order: widget.state.order.copyWith( + subtotal: widget.state.totalAmount, + totalAmount: widget.state.totalAmount, + ), + ), + ); + } else if (widget.state.totalAmount > totalAmount) { + AppFlushbar.showError( + context, + "Jumlah split tidak boleh melebihi total bill", + ); + } else { + AppFlushbar.showError(context, "Total bayar harus lebih dari 0"); + } + } + } +} diff --git a/lib/presentation/router/app_router.dart b/lib/presentation/router/app_router.dart index 7284a0e..f7005b9 100644 --- a/lib/presentation/router/app_router.dart +++ b/lib/presentation/router/app_router.dart @@ -41,5 +41,8 @@ class AppRouter extends RootStackRouter { // Void AutoRoute(page: VoidRoute.page), AutoRoute(page: VoidSuccessRoute.page), + + // Split Bill + AutoRoute(page: SplitBillRoute.page), ]; } diff --git a/lib/presentation/router/app_router.gr.dart b/lib/presentation/router/app_router.gr.dart index d7be3f4..27c60e6 100644 --- a/lib/presentation/router/app_router.gr.dart +++ b/lib/presentation/router/app_router.gr.dart @@ -9,7 +9,7 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:apskel_pos_flutter_v2/domain/order/order.dart' as _i20; +import 'package:apskel_pos_flutter_v2/domain/order/order.dart' as _i21; import 'package:apskel_pos_flutter_v2/presentation/pages/auth/login/login_page.dart' as _i4; import 'package:apskel_pos_flutter_v2/presentation/pages/checkout/checkout_page.dart' @@ -25,101 +25,103 @@ import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/report/repor import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/setting/setting_page.dart' as _i10; import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/table/table_page.dart' - as _i15; + as _i16; import 'package:apskel_pos_flutter_v2/presentation/pages/order/order_page.dart' as _i6; import 'package:apskel_pos_flutter_v2/presentation/pages/order/pages/success_add_item_order/success_add_item_order_page.dart' - as _i12; -import 'package:apskel_pos_flutter_v2/presentation/pages/order/pages/success_order/success_order_page.dart' as _i13; +import 'package:apskel_pos_flutter_v2/presentation/pages/order/pages/success_order/success_order_page.dart' + as _i14; import 'package:apskel_pos_flutter_v2/presentation/pages/payment/pages/payment_success/payment_success_page.dart' as _i8; import 'package:apskel_pos_flutter_v2/presentation/pages/payment/payment_page.dart' as _i7; import 'package:apskel_pos_flutter_v2/presentation/pages/splash/splash_page.dart' as _i11; +import 'package:apskel_pos_flutter_v2/presentation/pages/split_bill/split_bill_page.dart' + as _i12; import 'package:apskel_pos_flutter_v2/presentation/pages/sync/sync_page.dart' - as _i14; + as _i15; import 'package:apskel_pos_flutter_v2/presentation/pages/void/pages/void_success/void_success_page.dart' - as _i17; + as _i18; import 'package:apskel_pos_flutter_v2/presentation/pages/void/void_page.dart' - as _i16; -import 'package:auto_route/auto_route.dart' as _i18; -import 'package:flutter/material.dart' as _i19; + as _i17; +import 'package:auto_route/auto_route.dart' as _i19; +import 'package:flutter/material.dart' as _i20; /// generated route for /// [_i1.CheckoutPage] -class CheckoutRoute extends _i18.PageRouteInfo { - const CheckoutRoute({List<_i18.PageRouteInfo>? children}) +class CheckoutRoute extends _i19.PageRouteInfo { + const CheckoutRoute({List<_i19.PageRouteInfo>? children}) : super(CheckoutRoute.name, initialChildren: children); static const String name = 'CheckoutRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return _i18.WrappedRoute(child: const _i1.CheckoutPage()); + return _i19.WrappedRoute(child: const _i1.CheckoutPage()); }, ); } /// generated route for /// [_i2.CustomerPage] -class CustomerRoute extends _i18.PageRouteInfo { - const CustomerRoute({List<_i18.PageRouteInfo>? children}) +class CustomerRoute extends _i19.PageRouteInfo { + const CustomerRoute({List<_i19.PageRouteInfo>? children}) : super(CustomerRoute.name, initialChildren: children); static const String name = 'CustomerRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return _i18.WrappedRoute(child: const _i2.CustomerPage()); + return _i19.WrappedRoute(child: const _i2.CustomerPage()); }, ); } /// generated route for /// [_i3.HomePage] -class HomeRoute extends _i18.PageRouteInfo { - const HomeRoute({List<_i18.PageRouteInfo>? children}) +class HomeRoute extends _i19.PageRouteInfo { + const HomeRoute({List<_i19.PageRouteInfo>? children}) : super(HomeRoute.name, initialChildren: children); static const String name = 'HomeRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return _i18.WrappedRoute(child: const _i3.HomePage()); + return _i19.WrappedRoute(child: const _i3.HomePage()); }, ); } /// generated route for /// [_i4.LoginPage] -class LoginRoute extends _i18.PageRouteInfo { - const LoginRoute({List<_i18.PageRouteInfo>? children}) +class LoginRoute extends _i19.PageRouteInfo { + const LoginRoute({List<_i19.PageRouteInfo>? children}) : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return _i18.WrappedRoute(child: const _i4.LoginPage()); + return _i19.WrappedRoute(child: const _i4.LoginPage()); }, ); } /// generated route for /// [_i5.MainPage] -class MainRoute extends _i18.PageRouteInfo { - const MainRoute({List<_i18.PageRouteInfo>? children}) +class MainRoute extends _i19.PageRouteInfo { + const MainRoute({List<_i19.PageRouteInfo>? children}) : super(MainRoute.name, initialChildren: children); static const String name = 'MainRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { return const _i5.MainPage(); @@ -129,11 +131,11 @@ class MainRoute extends _i18.PageRouteInfo { /// generated route for /// [_i6.OrderPage] -class OrderRoute extends _i18.PageRouteInfo { +class OrderRoute extends _i19.PageRouteInfo { OrderRoute({ - _i19.Key? key, + _i20.Key? key, required String status, - List<_i18.PageRouteInfo>? children, + List<_i19.PageRouteInfo>? children, }) : super( OrderRoute.name, args: OrderRouteArgs(key: key, status: status), @@ -142,11 +144,11 @@ class OrderRoute extends _i18.PageRouteInfo { static const String name = 'OrderRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i18.WrappedRoute( + return _i19.WrappedRoute( child: _i6.OrderPage(key: args.key, status: args.status), ); }, @@ -156,7 +158,7 @@ class OrderRoute extends _i18.PageRouteInfo { class OrderRouteArgs { const OrderRouteArgs({this.key, required this.status}); - final _i19.Key? key; + final _i20.Key? key; final String status; @@ -168,11 +170,11 @@ class OrderRouteArgs { /// generated route for /// [_i7.PaymentPage] -class PaymentRoute extends _i18.PageRouteInfo { +class PaymentRoute extends _i19.PageRouteInfo { PaymentRoute({ - _i19.Key? key, - required _i20.Order order, - List<_i18.PageRouteInfo>? children, + _i20.Key? key, + required _i21.Order order, + List<_i19.PageRouteInfo>? children, }) : super( PaymentRoute.name, args: PaymentRouteArgs(key: key, order: order), @@ -181,11 +183,11 @@ class PaymentRoute extends _i18.PageRouteInfo { static const String name = 'PaymentRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i18.WrappedRoute( + return _i19.WrappedRoute( child: _i7.PaymentPage(key: args.key, order: args.order), ); }, @@ -195,9 +197,9 @@ class PaymentRoute extends _i18.PageRouteInfo { class PaymentRouteArgs { const PaymentRouteArgs({this.key, required this.order}); - final _i19.Key? key; + final _i20.Key? key; - final _i20.Order order; + final _i21.Order order; @override String toString() { @@ -207,11 +209,11 @@ class PaymentRouteArgs { /// generated route for /// [_i8.PaymentSuccessPage] -class PaymentSuccessRoute extends _i18.PageRouteInfo { +class PaymentSuccessRoute extends _i19.PageRouteInfo { PaymentSuccessRoute({ - _i19.Key? key, + _i20.Key? key, required String orderId, - List<_i18.PageRouteInfo>? children, + List<_i19.PageRouteInfo>? children, }) : super( PaymentSuccessRoute.name, args: PaymentSuccessRouteArgs(key: key, orderId: orderId), @@ -220,11 +222,11 @@ class PaymentSuccessRoute extends _i18.PageRouteInfo { static const String name = 'PaymentSuccessRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i18.WrappedRoute( + return _i19.WrappedRoute( child: _i8.PaymentSuccessPage(key: args.key, orderId: args.orderId), ); }, @@ -234,7 +236,7 @@ class PaymentSuccessRoute extends _i18.PageRouteInfo { class PaymentSuccessRouteArgs { const PaymentSuccessRouteArgs({this.key, required this.orderId}); - final _i19.Key? key; + final _i20.Key? key; final String orderId; @@ -246,13 +248,13 @@ class PaymentSuccessRouteArgs { /// generated route for /// [_i9.ReportPage] -class ReportRoute extends _i18.PageRouteInfo { - const ReportRoute({List<_i18.PageRouteInfo>? children}) +class ReportRoute extends _i19.PageRouteInfo { + const ReportRoute({List<_i19.PageRouteInfo>? children}) : super(ReportRoute.name, initialChildren: children); static const String name = 'ReportRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { return const _i9.ReportPage(); @@ -262,13 +264,13 @@ class ReportRoute extends _i18.PageRouteInfo { /// generated route for /// [_i10.SettingPage] -class SettingRoute extends _i18.PageRouteInfo { - const SettingRoute({List<_i18.PageRouteInfo>? children}) +class SettingRoute extends _i19.PageRouteInfo { + const SettingRoute({List<_i19.PageRouteInfo>? children}) : super(SettingRoute.name, initialChildren: children); static const String name = 'SettingRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { return const _i10.SettingPage(); @@ -278,13 +280,13 @@ class SettingRoute extends _i18.PageRouteInfo { /// generated route for /// [_i11.SplashPage] -class SplashRoute extends _i18.PageRouteInfo { - const SplashRoute({List<_i18.PageRouteInfo>? children}) +class SplashRoute extends _i19.PageRouteInfo { + const SplashRoute({List<_i19.PageRouteInfo>? children}) : super(SplashRoute.name, initialChildren: children); static const String name = 'SplashRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { return const _i11.SplashPage(); @@ -293,28 +295,67 @@ class SplashRoute extends _i18.PageRouteInfo { } /// generated route for -/// [_i12.SuccessAddItemOrderPage] -class SuccessAddItemOrderRoute extends _i18.PageRouteInfo { - const SuccessAddItemOrderRoute({List<_i18.PageRouteInfo>? children}) +/// [_i12.SplitBillPage] +class SplitBillRoute extends _i19.PageRouteInfo { + SplitBillRoute({ + _i20.Key? key, + required _i21.Order order, + List<_i19.PageRouteInfo>? children, + }) : super( + SplitBillRoute.name, + args: SplitBillRouteArgs(key: key, order: order), + initialChildren: children, + ); + + static const String name = 'SplitBillRoute'; + + static _i19.PageInfo page = _i19.PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return _i19.WrappedRoute( + child: _i12.SplitBillPage(key: args.key, order: args.order), + ); + }, + ); +} + +class SplitBillRouteArgs { + const SplitBillRouteArgs({this.key, required this.order}); + + final _i20.Key? key; + + final _i21.Order order; + + @override + String toString() { + return 'SplitBillRouteArgs{key: $key, order: $order}'; + } +} + +/// generated route for +/// [_i13.SuccessAddItemOrderPage] +class SuccessAddItemOrderRoute extends _i19.PageRouteInfo { + const SuccessAddItemOrderRoute({List<_i19.PageRouteInfo>? children}) : super(SuccessAddItemOrderRoute.name, initialChildren: children); static const String name = 'SuccessAddItemOrderRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return const _i12.SuccessAddItemOrderPage(); + return const _i13.SuccessAddItemOrderPage(); }, ); } /// generated route for -/// [_i13.SuccessOrderPage] -class SuccessOrderRoute extends _i18.PageRouteInfo { +/// [_i14.SuccessOrderPage] +class SuccessOrderRoute extends _i19.PageRouteInfo { SuccessOrderRoute({ - _i19.Key? key, - required _i20.Order order, - List<_i18.PageRouteInfo>? children, + _i20.Key? key, + required _i21.Order order, + List<_i19.PageRouteInfo>? children, }) : super( SuccessOrderRoute.name, args: SuccessOrderRouteArgs(key: key, order: order), @@ -323,12 +364,12 @@ class SuccessOrderRoute extends _i18.PageRouteInfo { static const String name = 'SuccessOrderRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i18.WrappedRoute( - child: _i13.SuccessOrderPage(key: args.key, order: args.order), + return _i19.WrappedRoute( + child: _i14.SuccessOrderPage(key: args.key, order: args.order), ); }, ); @@ -337,9 +378,9 @@ class SuccessOrderRoute extends _i18.PageRouteInfo { class SuccessOrderRouteArgs { const SuccessOrderRouteArgs({this.key, required this.order}); - final _i19.Key? key; + final _i20.Key? key; - final _i20.Order order; + final _i21.Order order; @override String toString() { @@ -348,44 +389,44 @@ class SuccessOrderRouteArgs { } /// generated route for -/// [_i14.SyncPage] -class SyncRoute extends _i18.PageRouteInfo { - const SyncRoute({List<_i18.PageRouteInfo>? children}) +/// [_i15.SyncPage] +class SyncRoute extends _i19.PageRouteInfo { + const SyncRoute({List<_i19.PageRouteInfo>? children}) : super(SyncRoute.name, initialChildren: children); static const String name = 'SyncRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return _i18.WrappedRoute(child: const _i14.SyncPage()); + return _i19.WrappedRoute(child: const _i15.SyncPage()); }, ); } /// generated route for -/// [_i15.TablePage] -class TableRoute extends _i18.PageRouteInfo { - const TableRoute({List<_i18.PageRouteInfo>? children}) +/// [_i16.TablePage] +class TableRoute extends _i19.PageRouteInfo { + const TableRoute({List<_i19.PageRouteInfo>? children}) : super(TableRoute.name, initialChildren: children); static const String name = 'TableRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return _i18.WrappedRoute(child: const _i15.TablePage()); + return _i19.WrappedRoute(child: const _i16.TablePage()); }, ); } /// generated route for -/// [_i16.VoidPage] -class VoidRoute extends _i18.PageRouteInfo { +/// [_i17.VoidPage] +class VoidRoute extends _i19.PageRouteInfo { VoidRoute({ - _i19.Key? key, - required _i20.Order order, - List<_i18.PageRouteInfo>? children, + _i20.Key? key, + required _i21.Order order, + List<_i19.PageRouteInfo>? children, }) : super( VoidRoute.name, args: VoidRouteArgs(key: key, order: order), @@ -394,12 +435,12 @@ class VoidRoute extends _i18.PageRouteInfo { static const String name = 'VoidRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i18.WrappedRoute( - child: _i16.VoidPage(key: args.key, order: args.order), + return _i19.WrappedRoute( + child: _i17.VoidPage(key: args.key, order: args.order), ); }, ); @@ -408,9 +449,9 @@ class VoidRoute extends _i18.PageRouteInfo { class VoidRouteArgs { const VoidRouteArgs({this.key, required this.order}); - final _i19.Key? key; + final _i20.Key? key; - final _i20.Order order; + final _i21.Order order; @override String toString() { @@ -419,17 +460,17 @@ class VoidRouteArgs { } /// generated route for -/// [_i17.VoidSuccessPage] -class VoidSuccessRoute extends _i18.PageRouteInfo { - const VoidSuccessRoute({List<_i18.PageRouteInfo>? children}) +/// [_i18.VoidSuccessPage] +class VoidSuccessRoute extends _i19.PageRouteInfo { + const VoidSuccessRoute({List<_i19.PageRouteInfo>? children}) : super(VoidSuccessRoute.name, initialChildren: children); static const String name = 'VoidSuccessRoute'; - static _i18.PageInfo page = _i18.PageInfo( + static _i19.PageInfo page = _i19.PageInfo( name, builder: (data) { - return const _i17.VoidSuccessPage(); + return const _i18.VoidSuccessPage(); }, ); }