From 5c683eda8d49800bf79b5ccd622434ec7c564548 Mon Sep 17 00:00:00 2001 From: efrilm Date: Sun, 26 Oct 2025 19:36:59 +0700 Subject: [PATCH] transfer table --- .../table/table_form/table_form_bloc.dart | 26 ++ .../table_form/table_form_bloc.freezed.dart | 253 +++++++++++++++- .../table/table_form/table_form_event.dart | 4 + .../table/table_form/table_form_state.dart | 10 +- .../table/table_loader/table_loader_bloc.dart | 12 +- .../table_loader_bloc.freezed.dart | 53 ++-- .../table_loader/table_loader_event.dart | 6 +- .../repositories/i_table_repository.dart | 6 + .../datasources/remote_data_provider.dart | 32 +- .../table/repositories/table_repository.dart | 24 ++ lib/injection.config.dart | 6 +- .../dialog/table/table_transfer_dialog.dart | 284 ++++++++++++++++++ .../pages/main/pages/table/table_page.dart | 65 ++-- pubspec.lock | 8 + pubspec.yaml | 1 + 15 files changed, 736 insertions(+), 54 deletions(-) create mode 100644 lib/presentation/components/dialog/table/table_transfer_dialog.dart diff --git a/lib/application/table/table_form/table_form_bloc.dart b/lib/application/table/table_form/table_form_bloc.dart index 6e65fd1..c28f027 100644 --- a/lib/application/table/table_form/table_form_bloc.dart +++ b/lib/application/table/table_form/table_form_bloc.dart @@ -65,6 +65,32 @@ class TableFormBloc extends Bloc { ), ); }, + transfer: (e) async { + Either failureOrTableTransfer; + + emit( + state.copyWith(isTransfering: true, failureOrTableTransfer: none()), + ); + + final isFromTableIdValid = e.fromTableId.isNotEmpty; + final isToTableIdValid = e.toTableId.isNotEmpty; + + if (isFromTableIdValid && isToTableIdValid) { + failureOrTableTransfer = await _repository.transferTable( + fromTableId: e.fromTableId, + toTableId: e.toTableId, + ); + + emit( + state.copyWith( + failureOrTableTransfer: optionOf(failureOrTableTransfer), + isTransfering: false, + ), + ); + } + + emit(state.copyWith(failureOrTable: none(), isCreating: false)); + }, ); } } diff --git a/lib/application/table/table_form/table_form_bloc.freezed.dart b/lib/application/table/table_form/table_form_bloc.freezed.dart index 121e06a..c60d162 100644 --- a/lib/application/table/table_form/table_form_bloc.freezed.dart +++ b/lib/application/table/table_form/table_form_bloc.freezed.dart @@ -23,6 +23,7 @@ mixin _$TableFormEvent { required TResult Function(String capacity) capacityChanged, required TResult Function() created, required TResult Function(String id, Offset position) updated, + required TResult Function(String fromTableId, String toTableId) transfer, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ @@ -30,6 +31,7 @@ mixin _$TableFormEvent { TResult? Function(String capacity)? capacityChanged, TResult? Function()? created, TResult? Function(String id, Offset position)? updated, + TResult? Function(String fromTableId, String toTableId)? transfer, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ @@ -37,6 +39,7 @@ mixin _$TableFormEvent { TResult Function(String capacity)? capacityChanged, TResult Function()? created, TResult Function(String id, Offset position)? updated, + TResult Function(String fromTableId, String toTableId)? transfer, required TResult orElse(), }) => throw _privateConstructorUsedError; @optionalTypeArgs @@ -45,6 +48,7 @@ mixin _$TableFormEvent { required TResult Function(_CapacityChanged value) capacityChanged, required TResult Function(_Created value) created, required TResult Function(_Updated value) updated, + required TResult Function(_Transfer value) transfer, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? mapOrNull({ @@ -52,6 +56,7 @@ mixin _$TableFormEvent { TResult? Function(_CapacityChanged value)? capacityChanged, TResult? Function(_Created value)? created, TResult? Function(_Updated value)? updated, + TResult? Function(_Transfer value)? transfer, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeMap({ @@ -59,6 +64,7 @@ mixin _$TableFormEvent { TResult Function(_CapacityChanged value)? capacityChanged, TResult Function(_Created value)? created, TResult Function(_Updated value)? updated, + TResult Function(_Transfer value)? transfer, required TResult orElse(), }) => throw _privateConstructorUsedError; } @@ -159,6 +165,7 @@ class _$NameChangedImpl implements _NameChanged { required TResult Function(String capacity) capacityChanged, required TResult Function() created, required TResult Function(String id, Offset position) updated, + required TResult Function(String fromTableId, String toTableId) transfer, }) { return nameChanged(name); } @@ -170,6 +177,7 @@ class _$NameChangedImpl implements _NameChanged { TResult? Function(String capacity)? capacityChanged, TResult? Function()? created, TResult? Function(String id, Offset position)? updated, + TResult? Function(String fromTableId, String toTableId)? transfer, }) { return nameChanged?.call(name); } @@ -181,6 +189,7 @@ class _$NameChangedImpl implements _NameChanged { TResult Function(String capacity)? capacityChanged, TResult Function()? created, TResult Function(String id, Offset position)? updated, + TResult Function(String fromTableId, String toTableId)? transfer, required TResult orElse(), }) { if (nameChanged != null) { @@ -196,6 +205,7 @@ class _$NameChangedImpl implements _NameChanged { required TResult Function(_CapacityChanged value) capacityChanged, required TResult Function(_Created value) created, required TResult Function(_Updated value) updated, + required TResult Function(_Transfer value) transfer, }) { return nameChanged(this); } @@ -207,6 +217,7 @@ class _$NameChangedImpl implements _NameChanged { TResult? Function(_CapacityChanged value)? capacityChanged, TResult? Function(_Created value)? created, TResult? Function(_Updated value)? updated, + TResult? Function(_Transfer value)? transfer, }) { return nameChanged?.call(this); } @@ -218,6 +229,7 @@ class _$NameChangedImpl implements _NameChanged { TResult Function(_CapacityChanged value)? capacityChanged, TResult Function(_Created value)? created, TResult Function(_Updated value)? updated, + TResult Function(_Transfer value)? transfer, required TResult orElse(), }) { if (nameChanged != null) { @@ -317,6 +329,7 @@ class _$CapacityChangedImpl implements _CapacityChanged { required TResult Function(String capacity) capacityChanged, required TResult Function() created, required TResult Function(String id, Offset position) updated, + required TResult Function(String fromTableId, String toTableId) transfer, }) { return capacityChanged(capacity); } @@ -328,6 +341,7 @@ class _$CapacityChangedImpl implements _CapacityChanged { TResult? Function(String capacity)? capacityChanged, TResult? Function()? created, TResult? Function(String id, Offset position)? updated, + TResult? Function(String fromTableId, String toTableId)? transfer, }) { return capacityChanged?.call(capacity); } @@ -339,6 +353,7 @@ class _$CapacityChangedImpl implements _CapacityChanged { TResult Function(String capacity)? capacityChanged, TResult Function()? created, TResult Function(String id, Offset position)? updated, + TResult Function(String fromTableId, String toTableId)? transfer, required TResult orElse(), }) { if (capacityChanged != null) { @@ -354,6 +369,7 @@ class _$CapacityChangedImpl implements _CapacityChanged { required TResult Function(_CapacityChanged value) capacityChanged, required TResult Function(_Created value) created, required TResult Function(_Updated value) updated, + required TResult Function(_Transfer value) transfer, }) { return capacityChanged(this); } @@ -365,6 +381,7 @@ class _$CapacityChangedImpl implements _CapacityChanged { TResult? Function(_CapacityChanged value)? capacityChanged, TResult? Function(_Created value)? created, TResult? Function(_Updated value)? updated, + TResult? Function(_Transfer value)? transfer, }) { return capacityChanged?.call(this); } @@ -376,6 +393,7 @@ class _$CapacityChangedImpl implements _CapacityChanged { TResult Function(_CapacityChanged value)? capacityChanged, TResult Function(_Created value)? created, TResult Function(_Updated value)? updated, + TResult Function(_Transfer value)? transfer, required TResult orElse(), }) { if (capacityChanged != null) { @@ -444,6 +462,7 @@ class _$CreatedImpl implements _Created { required TResult Function(String capacity) capacityChanged, required TResult Function() created, required TResult Function(String id, Offset position) updated, + required TResult Function(String fromTableId, String toTableId) transfer, }) { return created(); } @@ -455,6 +474,7 @@ class _$CreatedImpl implements _Created { TResult? Function(String capacity)? capacityChanged, TResult? Function()? created, TResult? Function(String id, Offset position)? updated, + TResult? Function(String fromTableId, String toTableId)? transfer, }) { return created?.call(); } @@ -466,6 +486,7 @@ class _$CreatedImpl implements _Created { TResult Function(String capacity)? capacityChanged, TResult Function()? created, TResult Function(String id, Offset position)? updated, + TResult Function(String fromTableId, String toTableId)? transfer, required TResult orElse(), }) { if (created != null) { @@ -481,6 +502,7 @@ class _$CreatedImpl implements _Created { required TResult Function(_CapacityChanged value) capacityChanged, required TResult Function(_Created value) created, required TResult Function(_Updated value) updated, + required TResult Function(_Transfer value) transfer, }) { return created(this); } @@ -492,6 +514,7 @@ class _$CreatedImpl implements _Created { TResult? Function(_CapacityChanged value)? capacityChanged, TResult? Function(_Created value)? created, TResult? Function(_Updated value)? updated, + TResult? Function(_Transfer value)? transfer, }) { return created?.call(this); } @@ -503,6 +526,7 @@ class _$CreatedImpl implements _Created { TResult Function(_CapacityChanged value)? capacityChanged, TResult Function(_Created value)? created, TResult Function(_Updated value)? updated, + TResult Function(_Transfer value)? transfer, required TResult orElse(), }) { if (created != null) { @@ -598,6 +622,7 @@ class _$UpdatedImpl implements _Updated { required TResult Function(String capacity) capacityChanged, required TResult Function() created, required TResult Function(String id, Offset position) updated, + required TResult Function(String fromTableId, String toTableId) transfer, }) { return updated(id, position); } @@ -609,6 +634,7 @@ class _$UpdatedImpl implements _Updated { TResult? Function(String capacity)? capacityChanged, TResult? Function()? created, TResult? Function(String id, Offset position)? updated, + TResult? Function(String fromTableId, String toTableId)? transfer, }) { return updated?.call(id, position); } @@ -620,6 +646,7 @@ class _$UpdatedImpl implements _Updated { TResult Function(String capacity)? capacityChanged, TResult Function()? created, TResult Function(String id, Offset position)? updated, + TResult Function(String fromTableId, String toTableId)? transfer, required TResult orElse(), }) { if (updated != null) { @@ -635,6 +662,7 @@ class _$UpdatedImpl implements _Updated { required TResult Function(_CapacityChanged value) capacityChanged, required TResult Function(_Created value) created, required TResult Function(_Updated value) updated, + required TResult Function(_Transfer value) transfer, }) { return updated(this); } @@ -646,6 +674,7 @@ class _$UpdatedImpl implements _Updated { TResult? Function(_CapacityChanged value)? capacityChanged, TResult? Function(_Created value)? created, TResult? Function(_Updated value)? updated, + TResult? Function(_Transfer value)? transfer, }) { return updated?.call(this); } @@ -657,6 +686,7 @@ class _$UpdatedImpl implements _Updated { TResult Function(_CapacityChanged value)? capacityChanged, TResult Function(_Created value)? created, TResult Function(_Updated value)? updated, + TResult Function(_Transfer value)? transfer, required TResult orElse(), }) { if (updated != null) { @@ -682,14 +712,190 @@ abstract class _Updated implements TableFormEvent { throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$TransferImplCopyWith<$Res> { + factory _$$TransferImplCopyWith( + _$TransferImpl value, + $Res Function(_$TransferImpl) then, + ) = __$$TransferImplCopyWithImpl<$Res>; + @useResult + $Res call({String fromTableId, String toTableId}); +} + +/// @nodoc +class __$$TransferImplCopyWithImpl<$Res> + extends _$TableFormEventCopyWithImpl<$Res, _$TransferImpl> + implements _$$TransferImplCopyWith<$Res> { + __$$TransferImplCopyWithImpl( + _$TransferImpl _value, + $Res Function(_$TransferImpl) _then, + ) : super(_value, _then); + + /// Create a copy of TableFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? fromTableId = null, Object? toTableId = null}) { + return _then( + _$TransferImpl( + fromTableId: null == fromTableId + ? _value.fromTableId + : fromTableId // ignore: cast_nullable_to_non_nullable + as String, + toTableId: null == toTableId + ? _value.toTableId + : toTableId // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$TransferImpl implements _Transfer { + const _$TransferImpl({required this.fromTableId, required this.toTableId}); + + @override + final String fromTableId; + @override + final String toTableId; + + @override + String toString() { + return 'TableFormEvent.transfer(fromTableId: $fromTableId, toTableId: $toTableId)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TransferImpl && + (identical(other.fromTableId, fromTableId) || + other.fromTableId == fromTableId) && + (identical(other.toTableId, toTableId) || + other.toTableId == toTableId)); + } + + @override + int get hashCode => Object.hash(runtimeType, fromTableId, toTableId); + + /// Create a copy of TableFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TransferImplCopyWith<_$TransferImpl> get copyWith => + __$$TransferImplCopyWithImpl<_$TransferImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(String name) nameChanged, + required TResult Function(String capacity) capacityChanged, + required TResult Function() created, + required TResult Function(String id, Offset position) updated, + required TResult Function(String fromTableId, String toTableId) transfer, + }) { + return transfer(fromTableId, toTableId); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(String name)? nameChanged, + TResult? Function(String capacity)? capacityChanged, + TResult? Function()? created, + TResult? Function(String id, Offset position)? updated, + TResult? Function(String fromTableId, String toTableId)? transfer, + }) { + return transfer?.call(fromTableId, toTableId); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String name)? nameChanged, + TResult Function(String capacity)? capacityChanged, + TResult Function()? created, + TResult Function(String id, Offset position)? updated, + TResult Function(String fromTableId, String toTableId)? transfer, + required TResult orElse(), + }) { + if (transfer != null) { + return transfer(fromTableId, toTableId); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_NameChanged value) nameChanged, + required TResult Function(_CapacityChanged value) capacityChanged, + required TResult Function(_Created value) created, + required TResult Function(_Updated value) updated, + required TResult Function(_Transfer value) transfer, + }) { + return transfer(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_NameChanged value)? nameChanged, + TResult? Function(_CapacityChanged value)? capacityChanged, + TResult? Function(_Created value)? created, + TResult? Function(_Updated value)? updated, + TResult? Function(_Transfer value)? transfer, + }) { + return transfer?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_NameChanged value)? nameChanged, + TResult Function(_CapacityChanged value)? capacityChanged, + TResult Function(_Created value)? created, + TResult Function(_Updated value)? updated, + TResult Function(_Transfer value)? transfer, + required TResult orElse(), + }) { + if (transfer != null) { + return transfer(this); + } + return orElse(); + } +} + +abstract class _Transfer implements TableFormEvent { + const factory _Transfer({ + required final String fromTableId, + required final String toTableId, + }) = _$TransferImpl; + + String get fromTableId; + String get toTableId; + + /// Create a copy of TableFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TransferImplCopyWith<_$TransferImpl> get copyWith => + throw _privateConstructorUsedError; +} + /// @nodoc mixin _$TableFormState { String get name => throw _privateConstructorUsedError; int get capacity => throw _privateConstructorUsedError; Option> get failureOrTable => throw _privateConstructorUsedError; + Option> get failureOrTableTransfer => + throw _privateConstructorUsedError; bool get isUpdating => throw _privateConstructorUsedError; bool get isCreating => throw _privateConstructorUsedError; + bool get isTransfering => throw _privateConstructorUsedError; /// Create a copy of TableFormState /// with the given fields replaced by the non-null parameter values. @@ -709,8 +915,10 @@ abstract class $TableFormStateCopyWith<$Res> { String name, int capacity, Option> failureOrTable, + Option> failureOrTableTransfer, bool isUpdating, bool isCreating, + bool isTransfering, }); } @@ -732,8 +940,10 @@ class _$TableFormStateCopyWithImpl<$Res, $Val extends TableFormState> Object? name = null, Object? capacity = null, Object? failureOrTable = null, + Object? failureOrTableTransfer = null, Object? isUpdating = null, Object? isCreating = null, + Object? isTransfering = null, }) { return _then( _value.copyWith( @@ -749,6 +959,10 @@ class _$TableFormStateCopyWithImpl<$Res, $Val extends TableFormState> ? _value.failureOrTable : failureOrTable // ignore: cast_nullable_to_non_nullable as Option>, + failureOrTableTransfer: null == failureOrTableTransfer + ? _value.failureOrTableTransfer + : failureOrTableTransfer // ignore: cast_nullable_to_non_nullable + as Option>, isUpdating: null == isUpdating ? _value.isUpdating : isUpdating // ignore: cast_nullable_to_non_nullable @@ -757,6 +971,10 @@ class _$TableFormStateCopyWithImpl<$Res, $Val extends TableFormState> ? _value.isCreating : isCreating // ignore: cast_nullable_to_non_nullable as bool, + isTransfering: null == isTransfering + ? _value.isTransfering + : isTransfering // ignore: cast_nullable_to_non_nullable + as bool, ) as $Val, ); @@ -776,8 +994,10 @@ abstract class _$$TableFormStateImplCopyWith<$Res> String name, int capacity, Option> failureOrTable, + Option> failureOrTableTransfer, bool isUpdating, bool isCreating, + bool isTransfering, }); } @@ -798,8 +1018,10 @@ class __$$TableFormStateImplCopyWithImpl<$Res> Object? name = null, Object? capacity = null, Object? failureOrTable = null, + Object? failureOrTableTransfer = null, Object? isUpdating = null, Object? isCreating = null, + Object? isTransfering = null, }) { return _then( _$TableFormStateImpl( @@ -815,6 +1037,10 @@ class __$$TableFormStateImplCopyWithImpl<$Res> ? _value.failureOrTable : failureOrTable // ignore: cast_nullable_to_non_nullable as Option>, + failureOrTableTransfer: null == failureOrTableTransfer + ? _value.failureOrTableTransfer + : failureOrTableTransfer // ignore: cast_nullable_to_non_nullable + as Option>, isUpdating: null == isUpdating ? _value.isUpdating : isUpdating // ignore: cast_nullable_to_non_nullable @@ -823,6 +1049,10 @@ class __$$TableFormStateImplCopyWithImpl<$Res> ? _value.isCreating : isCreating // ignore: cast_nullable_to_non_nullable as bool, + isTransfering: null == isTransfering + ? _value.isTransfering + : isTransfering // ignore: cast_nullable_to_non_nullable + as bool, ), ); } @@ -835,8 +1065,10 @@ class _$TableFormStateImpl implements _TableFormState { required this.name, required this.capacity, required this.failureOrTable, + required this.failureOrTableTransfer, this.isUpdating = false, this.isCreating = false, + this.isTransfering = false, }); @override @@ -846,15 +1078,20 @@ class _$TableFormStateImpl implements _TableFormState { @override final Option> failureOrTable; @override + final Option> failureOrTableTransfer; + @override @JsonKey() final bool isUpdating; @override @JsonKey() final bool isCreating; + @override + @JsonKey() + final bool isTransfering; @override String toString() { - return 'TableFormState(name: $name, capacity: $capacity, failureOrTable: $failureOrTable, isUpdating: $isUpdating, isCreating: $isCreating)'; + return 'TableFormState(name: $name, capacity: $capacity, failureOrTable: $failureOrTable, failureOrTableTransfer: $failureOrTableTransfer, isUpdating: $isUpdating, isCreating: $isCreating, isTransfering: $isTransfering)'; } @override @@ -867,10 +1104,14 @@ class _$TableFormStateImpl implements _TableFormState { other.capacity == capacity) && (identical(other.failureOrTable, failureOrTable) || other.failureOrTable == failureOrTable) && + (identical(other.failureOrTableTransfer, failureOrTableTransfer) || + other.failureOrTableTransfer == failureOrTableTransfer) && (identical(other.isUpdating, isUpdating) || other.isUpdating == isUpdating) && (identical(other.isCreating, isCreating) || - other.isCreating == isCreating)); + other.isCreating == isCreating) && + (identical(other.isTransfering, isTransfering) || + other.isTransfering == isTransfering)); } @override @@ -879,8 +1120,10 @@ class _$TableFormStateImpl implements _TableFormState { name, capacity, failureOrTable, + failureOrTableTransfer, isUpdating, isCreating, + isTransfering, ); /// Create a copy of TableFormState @@ -900,8 +1143,10 @@ abstract class _TableFormState implements TableFormState { required final String name, required final int capacity, required final Option> failureOrTable, + required final Option> failureOrTableTransfer, final bool isUpdating, final bool isCreating, + final bool isTransfering, }) = _$TableFormStateImpl; @override @@ -911,9 +1156,13 @@ abstract class _TableFormState implements TableFormState { @override Option> get failureOrTable; @override + Option> get failureOrTableTransfer; + @override bool get isUpdating; @override bool get isCreating; + @override + bool get isTransfering; /// Create a copy of TableFormState /// with the given fields replaced by the non-null parameter values. diff --git a/lib/application/table/table_form/table_form_event.dart b/lib/application/table/table_form/table_form_event.dart index 86f4b24..a72223e 100644 --- a/lib/application/table/table_form/table_form_event.dart +++ b/lib/application/table/table_form/table_form_event.dart @@ -10,4 +10,8 @@ class TableFormEvent with _$TableFormEvent { required String id, required Offset position, }) = _Updated; + const factory TableFormEvent.transfer({ + required String fromTableId, + required String toTableId, + }) = _Transfer; } diff --git a/lib/application/table/table_form/table_form_state.dart b/lib/application/table/table_form/table_form_state.dart index e9dbf88..ef39bc6 100644 --- a/lib/application/table/table_form/table_form_state.dart +++ b/lib/application/table/table_form/table_form_state.dart @@ -6,10 +6,16 @@ class TableFormState with _$TableFormState { required String name, required int capacity, required Option> failureOrTable, + required Option> failureOrTableTransfer, @Default(false) bool isUpdating, @Default(false) bool isCreating, + @Default(false) bool isTransfering, }) = _TableFormState; - factory TableFormState.initial() => - TableFormState(failureOrTable: none(), name: '', capacity: 0); + factory TableFormState.initial() => TableFormState( + failureOrTable: none(), + name: '', + capacity: 0, + failureOrTableTransfer: none(), + ); } diff --git a/lib/application/table/table_loader/table_loader_bloc.dart b/lib/application/table/table_loader/table_loader_bloc.dart index 8242515..8030df0 100644 --- a/lib/application/table/table_loader/table_loader_bloc.dart +++ b/lib/application/table/table_loader/table_loader_bloc.dart @@ -40,7 +40,11 @@ class TableLoaderBloc extends Bloc { emit(newState); } - newState = await _mapFetchedToState(newState, isRefresh: e.isRefresh); + newState = await _mapFetchedToState( + newState, + isRefresh: e.isRefresh, + status: e.status, + ); emit(newState); }, updatedPostion: (e) async { @@ -62,6 +66,7 @@ class TableLoaderBloc extends Bloc { Future _mapFetchedToState( TableLoaderState state, { bool isRefresh = false, + String? status, }) async { state = state.copyWith(isFetching: false); @@ -78,7 +83,10 @@ class TableLoaderBloc extends Bloc { ); } - final failureOrTable = await _repository.fetchTables(page: state.page); + final failureOrTable = await _repository.fetchTables( + page: state.page, + status: status, + ); state = failureOrTable.fold( (f) { diff --git a/lib/application/table/table_loader/table_loader_bloc.freezed.dart b/lib/application/table/table_loader/table_loader_bloc.freezed.dart index 963d29f..0e0444e 100644 --- a/lib/application/table/table_loader/table_loader_bloc.freezed.dart +++ b/lib/application/table/table_loader/table_loader_bloc.freezed.dart @@ -19,19 +19,19 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$TableLoaderEvent { @optionalTypeArgs TResult when({ - required TResult Function(bool isRefresh) fetched, + required TResult Function(bool isRefresh, String? status) fetched, required TResult Function(String id, Offset position) updatedPostion, required TResult Function(Table? table) setSelectedTable, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(bool isRefresh)? fetched, + TResult? Function(bool isRefresh, String? status)? fetched, TResult? Function(String id, Offset position)? updatedPostion, TResult? Function(Table? table)? setSelectedTable, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ - TResult Function(bool isRefresh)? fetched, + TResult Function(bool isRefresh, String? status)? fetched, TResult Function(String id, Offset position)? updatedPostion, TResult Function(Table? table)? setSelectedTable, required TResult orElse(), @@ -86,7 +86,7 @@ abstract class _$$FetchedImplCopyWith<$Res> { $Res Function(_$FetchedImpl) then, ) = __$$FetchedImplCopyWithImpl<$Res>; @useResult - $Res call({bool isRefresh}); + $Res call({bool isRefresh, String? status}); } /// @nodoc @@ -102,13 +102,17 @@ class __$$FetchedImplCopyWithImpl<$Res> /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override - $Res call({Object? isRefresh = null}) { + $Res call({Object? isRefresh = null, Object? status = freezed}) { return _then( _$FetchedImpl( isRefresh: null == isRefresh ? _value.isRefresh : isRefresh // ignore: cast_nullable_to_non_nullable as bool, + status: freezed == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as String?, ), ); } @@ -117,15 +121,17 @@ class __$$FetchedImplCopyWithImpl<$Res> /// @nodoc class _$FetchedImpl implements _Fetched { - const _$FetchedImpl({this.isRefresh = false}); + const _$FetchedImpl({this.isRefresh = false, this.status}); @override @JsonKey() final bool isRefresh; + @override + final String? status; @override String toString() { - return 'TableLoaderEvent.fetched(isRefresh: $isRefresh)'; + return 'TableLoaderEvent.fetched(isRefresh: $isRefresh, status: $status)'; } @override @@ -134,11 +140,12 @@ class _$FetchedImpl implements _Fetched { (other.runtimeType == runtimeType && other is _$FetchedImpl && (identical(other.isRefresh, isRefresh) || - other.isRefresh == isRefresh)); + other.isRefresh == isRefresh) && + (identical(other.status, status) || other.status == status)); } @override - int get hashCode => Object.hash(runtimeType, isRefresh); + int get hashCode => Object.hash(runtimeType, isRefresh, status); /// Create a copy of TableLoaderEvent /// with the given fields replaced by the non-null parameter values. @@ -151,33 +158,33 @@ class _$FetchedImpl implements _Fetched { @override @optionalTypeArgs TResult when({ - required TResult Function(bool isRefresh) fetched, + required TResult Function(bool isRefresh, String? status) fetched, required TResult Function(String id, Offset position) updatedPostion, required TResult Function(Table? table) setSelectedTable, }) { - return fetched(isRefresh); + return fetched(isRefresh, status); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(bool isRefresh)? fetched, + TResult? Function(bool isRefresh, String? status)? fetched, TResult? Function(String id, Offset position)? updatedPostion, TResult? Function(Table? table)? setSelectedTable, }) { - return fetched?.call(isRefresh); + return fetched?.call(isRefresh, status); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(bool isRefresh)? fetched, + TResult Function(bool isRefresh, String? status)? fetched, TResult Function(String id, Offset position)? updatedPostion, TResult Function(Table? table)? setSelectedTable, required TResult orElse(), }) { if (fetched != null) { - return fetched(isRefresh); + return fetched(isRefresh, status); } return orElse(); } @@ -218,9 +225,11 @@ class _$FetchedImpl implements _Fetched { } abstract class _Fetched implements TableLoaderEvent { - const factory _Fetched({final bool isRefresh}) = _$FetchedImpl; + const factory _Fetched({final bool isRefresh, final String? status}) = + _$FetchedImpl; bool get isRefresh; + String? get status; /// Create a copy of TableLoaderEvent /// with the given fields replaced by the non-null parameter values. @@ -310,7 +319,7 @@ class _$UpdatedPositionImpl implements _UpdatedPosition { @override @optionalTypeArgs TResult when({ - required TResult Function(bool isRefresh) fetched, + required TResult Function(bool isRefresh, String? status) fetched, required TResult Function(String id, Offset position) updatedPostion, required TResult Function(Table? table) setSelectedTable, }) { @@ -320,7 +329,7 @@ class _$UpdatedPositionImpl implements _UpdatedPosition { @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(bool isRefresh)? fetched, + TResult? Function(bool isRefresh, String? status)? fetched, TResult? Function(String id, Offset position)? updatedPostion, TResult? Function(Table? table)? setSelectedTable, }) { @@ -330,7 +339,7 @@ class _$UpdatedPositionImpl implements _UpdatedPosition { @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(bool isRefresh)? fetched, + TResult Function(bool isRefresh, String? status)? fetched, TResult Function(String id, Offset position)? updatedPostion, TResult Function(Table? table)? setSelectedTable, required TResult orElse(), @@ -481,7 +490,7 @@ class _$SetSelectedTableImpl implements _SetSelectedTable { @override @optionalTypeArgs TResult when({ - required TResult Function(bool isRefresh) fetched, + required TResult Function(bool isRefresh, String? status) fetched, required TResult Function(String id, Offset position) updatedPostion, required TResult Function(Table? table) setSelectedTable, }) { @@ -491,7 +500,7 @@ class _$SetSelectedTableImpl implements _SetSelectedTable { @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(bool isRefresh)? fetched, + TResult? Function(bool isRefresh, String? status)? fetched, TResult? Function(String id, Offset position)? updatedPostion, TResult? Function(Table? table)? setSelectedTable, }) { @@ -501,7 +510,7 @@ class _$SetSelectedTableImpl implements _SetSelectedTable { @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(bool isRefresh)? fetched, + TResult Function(bool isRefresh, String? status)? fetched, TResult Function(String id, Offset position)? updatedPostion, TResult Function(Table? table)? setSelectedTable, required TResult orElse(), diff --git a/lib/application/table/table_loader/table_loader_event.dart b/lib/application/table/table_loader/table_loader_event.dart index b145f1a..ffab866 100644 --- a/lib/application/table/table_loader/table_loader_event.dart +++ b/lib/application/table/table_loader/table_loader_event.dart @@ -2,8 +2,10 @@ part of 'table_loader_bloc.dart'; @freezed class TableLoaderEvent with _$TableLoaderEvent { - const factory TableLoaderEvent.fetched({@Default(false) bool isRefresh}) = - _Fetched; + const factory TableLoaderEvent.fetched({ + @Default(false) bool isRefresh, + String? status, + }) = _Fetched; const factory TableLoaderEvent.updatedPostion({ required String id, required Offset position, diff --git a/lib/domain/table/repositories/i_table_repository.dart b/lib/domain/table/repositories/i_table_repository.dart index d2dcb9c..28b84e1 100644 --- a/lib/domain/table/repositories/i_table_repository.dart +++ b/lib/domain/table/repositories/i_table_repository.dart @@ -4,6 +4,7 @@ abstract class ITableRepository { Future> fetchTables({ int page = 1, int limit = 50, + String? status, }); Future> createTable({ @@ -15,4 +16,9 @@ abstract class ITableRepository { required String id, required Offset position, }); + + Future> transferTable({ + required String fromTableId, + required String toTableId, + }); } diff --git a/lib/infrastructure/table/datasources/remote_data_provider.dart b/lib/infrastructure/table/datasources/remote_data_provider.dart index db3114a..ef48982 100644 --- a/lib/infrastructure/table/datasources/remote_data_provider.dart +++ b/lib/infrastructure/table/datasources/remote_data_provider.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:dartz/dartz.dart'; import 'package:data_channel/data_channel.dart'; import 'package:flutter/widgets.dart'; import 'package:injectable/injectable.dart'; @@ -21,12 +22,19 @@ class TableRemoteDataProvider { Future> fetchTables({ int page = 1, int limit = 50, + String? status, }) async { try { + Map queryParameters = {'page': page, 'limit': limit}; + + if (status != null) { + queryParameters['status'] = status; + } + final response = await _apiClient.get( ApiPath.tables, headers: getAuthorizationHeader(), - params: {'page': page, 'limit': limit}, + params: queryParameters, ); if (response.data['data'] == null) { @@ -103,4 +111,26 @@ class TableRemoteDataProvider { return DC.error(TableFailure.serverError(e)); } } + + Future> transferTable({ + required String fromTableId, + required String toTableId, + }) async { + try { + final response = await _apiClient.put( + '${ApiPath.tables}/transfer', + data: {'from_table': fromTableId, 'to_table': toTableId}, + headers: getAuthorizationHeader(), + ); + + if (response.data['success'] == false) { + return DC.error(TableFailure.unexpectedError()); + } + + return DC.data(unit); + } on ApiFailure catch (e, s) { + log('transferTableError', name: _logName, error: e, stackTrace: s); + return DC.error(TableFailure.serverError(e)); + } + } } diff --git a/lib/infrastructure/table/repositories/table_repository.dart b/lib/infrastructure/table/repositories/table_repository.dart index 01477bb..4892889 100644 --- a/lib/infrastructure/table/repositories/table_repository.dart +++ b/lib/infrastructure/table/repositories/table_repository.dart @@ -20,11 +20,13 @@ class TableRepository implements ITableRepository { Future> fetchTables({ int page = 1, int limit = 50, + String? status, }) async { try { final result = await _remoteDataProvider.fetchTables( page: page, limit: limit, + status: status, ); if (result.hasError) { @@ -90,4 +92,26 @@ class TableRepository implements ITableRepository { return left(const TableFailure.unexpectedError()); } } + + @override + Future> transferTable({ + required String fromTableId, + required String toTableId, + }) async { + try { + final result = await _remoteDataProvider.transferTable( + fromTableId: fromTableId, + toTableId: toTableId, + ); + + if (result.hasError) { + return left(result.error!); + } + + return right(unit); + } catch (e) { + log('transferTableError', name: _logName, error: e); + return left(const TableFailure.unexpectedError()); + } + } } diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 7296fb6..8459d75 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -155,12 +155,12 @@ extension GetItInjectableX on _i174.GetIt { gh<_i693.OutletLocalDatasource>(), ), ); - gh.factory<_i424.TableLoaderBloc>( - () => _i424.TableLoaderBloc(gh<_i983.ITableRepository>()), - ); gh.factory<_i248.TableFormBloc>( () => _i248.TableFormBloc(gh<_i983.ITableRepository>()), ); + gh.factory<_i424.TableLoaderBloc>( + () => _i424.TableLoaderBloc(gh<_i983.ITableRepository>()), + ); gh.factory<_i44.IProductRepository>( () => _i763.ProductRepository( gh<_i707.ProductRemoteDataProvider>(), diff --git a/lib/presentation/components/dialog/table/table_transfer_dialog.dart b/lib/presentation/components/dialog/table/table_transfer_dialog.dart new file mode 100644 index 0000000..8f119d4 --- /dev/null +++ b/lib/presentation/components/dialog/table/table_transfer_dialog.dart @@ -0,0 +1,284 @@ +import 'dart:developer'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../application/table/table_form/table_form_bloc.dart'; +import '../../../../application/table/table_loader/table_loader_bloc.dart'; +import '../../../../common/extension/extension.dart'; +import '../../../../common/theme/theme.dart'; +import '../../../../domain/table/table.dart' as t; +import '../../button/button.dart'; +import '../../loader/loader_with_text.dart'; +import '../../spaces/space.dart'; +import '../../toast/flushbar.dart'; +import '../dialog.dart'; + +class TableTransferDialog extends StatefulWidget { + final t.Table fromTable; + const TableTransferDialog({super.key, required this.fromTable}); + + @override + State createState() => _TableTransferDialogState(); +} + +class _TableTransferDialogState extends State { + t.Table? selectTable; + + @override + void initState() { + super.initState(); + context.read().add( + TableLoaderEvent.fetched(isRefresh: true, status: 'available'), + ); + } + + @override + Widget build(BuildContext context) { + return CustomModalDialog( + title: 'Transfer Meja', + subtitle: 'Pilih meja yang tersedia', + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 24.0, + ), + minWidth: context.deviceWidth * 0.4, + minHeight: context.deviceHeight * 0.4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Pilih Meja', + style: AppStyle.lg.copyWith(fontWeight: FontWeight.w600), + ), + const SpaceHeight(6.0), + BlocBuilder( + builder: (context, state) { + final availableTables = state.tables; + + if (state.isFetching) { + return Center(child: const LoaderWithText()); + } + + if (selectTable == null && availableTables.isNotEmpty) { + selectTable = availableTables.first; + } + + if (availableTables.isEmpty) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.orange[50], + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.orange, width: 1), + ), + child: const Text( + 'Tidak ada meja yang tersedia. Silakan pilih opsi lain.', + style: TextStyle(color: Colors.orange), + ), + ); + } + + return DropdownSearch( + items: state.tables, + selectedItem: selectTable, + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + hintText: "Pilih meja", + hintStyle: TextStyle( + color: Colors.grey.shade600, + fontSize: 14, + ), + prefixIcon: Icon( + Icons.category_outlined, + color: Colors.grey.shade500, + size: 20, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.blue.shade400, + width: 2, + ), + ), + filled: true, + fillColor: Colors.white, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + ), + ), + popupProps: PopupProps.menu( + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Cari meja...", + prefixIcon: const Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + ), + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + itemBuilder: (context, category, isSelected) { + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + decoration: BoxDecoration( + color: isSelected + ? Colors.blue.shade50 + : Colors.transparent, + border: Border( + bottom: BorderSide( + color: Colors.grey.shade100, + width: 0.5, + ), + ), + ), + child: Row( + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: isSelected + ? Colors.blue.shade600 + : Colors.grey.shade400, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + category.tableName, + style: TextStyle( + fontSize: 14, + fontWeight: isSelected + ? FontWeight.w600 + : FontWeight.w500, + color: isSelected + ? Colors.blue.shade700 + : Colors.black87, + ), + ), + ), + if (isSelected) + Icon( + Icons.check, + color: Colors.blue.shade600, + size: 18, + ), + ], + ), + ); + }, + emptyBuilder: (context, searchEntry) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.search_off, + color: Colors.grey.shade400, + size: 48, + ), + const SizedBox(height: 12), + Text( + searchEntry.isEmpty + ? "Tidak ada meja tersedia" + : "Tidak ditemukan meja dengan '$searchEntry'", + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 14, + ), + textAlign: TextAlign.center, + ), + ], + ), + ); + }, + ), + itemAsString: (t.Table table) => table.tableName, + compareFn: (t.Table? item1, t.Table? item2) { + return item1?.id == item2?.id; + }, + onChanged: (t.Table? selectedTable) { + if (selectedTable != null) { + setState(() { + selectTable = selectedTable; + }); + log("selectTable: ${selectTable!.tableName}"); + } + }, + validator: (t.Table? value) { + if (value == null) { + return "Meja harus dipilih"; + } + return null; + }, + ); + }, + ), + ], + ), + SpaceHeight(24), + BlocBuilder( + builder: (context, state) { + return AppElevatedButton.filled( + onPressed: () { + if (selectTable == null) { + AppFlushbar.showError( + context, + 'Silahkan Pilih Meja Tujuan', + ); + return; + } + + context.read().add( + TableFormEvent.transfer( + fromTableId: widget.fromTable.id, + toTableId: selectTable!.id, + ), + ); + }, + label: "Transfer", + isLoading: state.isTransfering, + ); + }, + ), + ], + ), + ); + } +} diff --git a/lib/presentation/pages/main/pages/table/table_page.dart b/lib/presentation/pages/main/pages/table/table_page.dart index 56cde80..66d1930 100644 --- a/lib/presentation/pages/main/pages/table/table_page.dart +++ b/lib/presentation/pages/main/pages/table/table_page.dart @@ -9,6 +9,7 @@ import '../../../../../common/theme/theme.dart'; import '../../../../../domain/table/table.dart' as t; import '../../../../../injection.dart'; import '../../../../components/dialog/table/table_create_dialog.dart'; +import '../../../../components/dialog/table/table_transfer_dialog.dart'; import '../../../../components/loader/loader_with_text.dart'; import '../../../../components/toast/flushbar.dart'; import 'widgets/floating_bottom_navbar.dart'; @@ -44,23 +45,47 @@ class _TablePageState extends State { final double mapWidth = context.deviceWidth * 2; final double mapHeight = context.deviceHeight * 1.5; - return BlocListener( - listenWhen: (previous, current) => - previous.failureOrTable != current.failureOrTable, - listener: (context, state) { - state.failureOrTable.fold(() {}, (either) { - either.fold((f) => AppFlushbar.showTableFailureToast(context, f), ( - success, - ) { - if (context.mounted) { - context.router.maybePop(); - context.read().add( - TableLoaderEvent.fetched(isRefresh: true), + return MultiBlocListener( + listeners: [ + BlocListener( + listenWhen: (previous, current) => + previous.failureOrTable != current.failureOrTable, + listener: (context, state) { + state.failureOrTable.fold(() {}, (either) { + either.fold( + (f) => AppFlushbar.showTableFailureToast(context, f), + (success) { + if (context.mounted) { + context.router.maybePop(); + context.read().add( + TableLoaderEvent.fetched(isRefresh: true), + ); + } + }, ); - } - }); - }); - }, + }); + }, + ), + BlocListener( + listenWhen: (previous, current) => + previous.failureOrTableTransfer != current.failureOrTableTransfer, + listener: (context, state) { + state.failureOrTableTransfer.fold(() {}, (either) { + either.fold( + (f) => AppFlushbar.showTableFailureToast(context, f), + (success) { + if (context.mounted) { + context.router.maybePop(); + context.read().add( + TableLoaderEvent.fetched(isRefresh: true), + ); + } + }, + ); + }); + }, + ), + ], child: Scaffold( backgroundColor: AppColor.background, appBar: AppBar( @@ -297,10 +322,10 @@ class _TablePageState extends State { items: [ PopupMenuItem( onTap: () { - // showDialog( - // context: context, - // builder: (context) => TransferTableDialog(fromTable: table), - // ); + showDialog( + context: context, + builder: (context) => TableTransferDialog(fromTable: table), + ); }, child: Row( children: [ diff --git a/pubspec.lock b/pubspec.lock index 84180c9..74bcf10 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -345,6 +345,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + dropdown_search: + dependency: "direct main" + description: + name: dropdown_search + sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab" + url: "https://pub.dev" + source: hosted + version: "5.0.6" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 67bacde..555e83f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: sqflite: ^2.4.2 cached_network_image: ^3.4.1 shimmer: ^3.0.0 + dropdown_search: ^5.0.6 dev_dependencies: flutter_test: