update position table, set selected table

This commit is contained in:
efrilm 2025-10-26 18:06:09 +07:00
parent 655902a659
commit 634b331716
15 changed files with 1195 additions and 61 deletions

View File

@ -0,0 +1,45 @@
import 'dart:ui';
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/table/table.dart';
part 'table_form_event.dart';
part 'table_form_state.dart';
part 'table_form_bloc.freezed.dart';
@injectable
class TableFormBloc extends Bloc<TableFormEvent, TableFormState> {
final ITableRepository _repository;
TableFormBloc(this._repository) : super(TableFormState.initial()) {
on<TableFormEvent>(_onTableFormEvent);
}
Future<void> _onTableFormEvent(
TableFormEvent event,
Emitter<TableFormState> emit,
) {
return event.map(
updated: (e) async {
Either<TableFailure, Table> failureOrTable;
emit(state.copyWith(isUpdating: true, failureOrTable: none()));
failureOrTable = await _repository.updatePosition(
id: e.id,
position: e.position,
);
emit(
state.copyWith(
failureOrTable: optionOf(failureOrTable),
isUpdating: false,
),
);
},
);
}
}

View File

@ -0,0 +1,410 @@
// 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 'table_form_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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 _$TableFormEvent {
String get id => throw _privateConstructorUsedError;
Offset get position => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String id, Offset position) updated,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String id, Offset position)? updated,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String id, Offset position)? updated,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Updated value) updated,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Updated value)? updated,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Updated value)? updated,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
/// Create a copy of TableFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TableFormEventCopyWith<TableFormEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TableFormEventCopyWith<$Res> {
factory $TableFormEventCopyWith(
TableFormEvent value,
$Res Function(TableFormEvent) then,
) = _$TableFormEventCopyWithImpl<$Res, TableFormEvent>;
@useResult
$Res call({String id, Offset position});
}
/// @nodoc
class _$TableFormEventCopyWithImpl<$Res, $Val extends TableFormEvent>
implements $TableFormEventCopyWith<$Res> {
_$TableFormEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _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? id = null, Object? position = null}) {
return _then(
_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
position: null == position
? _value.position
: position // ignore: cast_nullable_to_non_nullable
as Offset,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$UpdatedImplCopyWith<$Res>
implements $TableFormEventCopyWith<$Res> {
factory _$$UpdatedImplCopyWith(
_$UpdatedImpl value,
$Res Function(_$UpdatedImpl) then,
) = __$$UpdatedImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String id, Offset position});
}
/// @nodoc
class __$$UpdatedImplCopyWithImpl<$Res>
extends _$TableFormEventCopyWithImpl<$Res, _$UpdatedImpl>
implements _$$UpdatedImplCopyWith<$Res> {
__$$UpdatedImplCopyWithImpl(
_$UpdatedImpl _value,
$Res Function(_$UpdatedImpl) _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? id = null, Object? position = null}) {
return _then(
_$UpdatedImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
position: null == position
? _value.position
: position // ignore: cast_nullable_to_non_nullable
as Offset,
),
);
}
}
/// @nodoc
class _$UpdatedImpl implements _Updated {
const _$UpdatedImpl({required this.id, required this.position});
@override
final String id;
@override
final Offset position;
@override
String toString() {
return 'TableFormEvent.updated(id: $id, position: $position)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$UpdatedImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.position, position) ||
other.position == position));
}
@override
int get hashCode => Object.hash(runtimeType, id, position);
/// 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')
_$$UpdatedImplCopyWith<_$UpdatedImpl> get copyWith =>
__$$UpdatedImplCopyWithImpl<_$UpdatedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String id, Offset position) updated,
}) {
return updated(id, position);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String id, Offset position)? updated,
}) {
return updated?.call(id, position);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String id, Offset position)? updated,
required TResult orElse(),
}) {
if (updated != null) {
return updated(id, position);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Updated value) updated,
}) {
return updated(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Updated value)? updated,
}) {
return updated?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Updated value)? updated,
required TResult orElse(),
}) {
if (updated != null) {
return updated(this);
}
return orElse();
}
}
abstract class _Updated implements TableFormEvent {
const factory _Updated({
required final String id,
required final Offset position,
}) = _$UpdatedImpl;
@override
String get id;
@override
Offset get position;
/// Create a copy of TableFormEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$UpdatedImplCopyWith<_$UpdatedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$TableFormState {
Option<Either<TableFailure, Table>> get failureOrTable =>
throw _privateConstructorUsedError;
bool get isUpdating => throw _privateConstructorUsedError;
/// Create a copy of TableFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TableFormStateCopyWith<TableFormState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TableFormStateCopyWith<$Res> {
factory $TableFormStateCopyWith(
TableFormState value,
$Res Function(TableFormState) then,
) = _$TableFormStateCopyWithImpl<$Res, TableFormState>;
@useResult
$Res call({
Option<Either<TableFailure, Table>> failureOrTable,
bool isUpdating,
});
}
/// @nodoc
class _$TableFormStateCopyWithImpl<$Res, $Val extends TableFormState>
implements $TableFormStateCopyWith<$Res> {
_$TableFormStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of TableFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? failureOrTable = null, Object? isUpdating = null}) {
return _then(
_value.copyWith(
failureOrTable: null == failureOrTable
? _value.failureOrTable
: failureOrTable // ignore: cast_nullable_to_non_nullable
as Option<Either<TableFailure, Table>>,
isUpdating: null == isUpdating
? _value.isUpdating
: isUpdating // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$TableFormStateImplCopyWith<$Res>
implements $TableFormStateCopyWith<$Res> {
factory _$$TableFormStateImplCopyWith(
_$TableFormStateImpl value,
$Res Function(_$TableFormStateImpl) then,
) = __$$TableFormStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
Option<Either<TableFailure, Table>> failureOrTable,
bool isUpdating,
});
}
/// @nodoc
class __$$TableFormStateImplCopyWithImpl<$Res>
extends _$TableFormStateCopyWithImpl<$Res, _$TableFormStateImpl>
implements _$$TableFormStateImplCopyWith<$Res> {
__$$TableFormStateImplCopyWithImpl(
_$TableFormStateImpl _value,
$Res Function(_$TableFormStateImpl) _then,
) : super(_value, _then);
/// Create a copy of TableFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? failureOrTable = null, Object? isUpdating = null}) {
return _then(
_$TableFormStateImpl(
failureOrTable: null == failureOrTable
? _value.failureOrTable
: failureOrTable // ignore: cast_nullable_to_non_nullable
as Option<Either<TableFailure, Table>>,
isUpdating: null == isUpdating
? _value.isUpdating
: isUpdating // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$TableFormStateImpl implements _TableFormState {
_$TableFormStateImpl({required this.failureOrTable, this.isUpdating = false});
@override
final Option<Either<TableFailure, Table>> failureOrTable;
@override
@JsonKey()
final bool isUpdating;
@override
String toString() {
return 'TableFormState(failureOrTable: $failureOrTable, isUpdating: $isUpdating)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TableFormStateImpl &&
(identical(other.failureOrTable, failureOrTable) ||
other.failureOrTable == failureOrTable) &&
(identical(other.isUpdating, isUpdating) ||
other.isUpdating == isUpdating));
}
@override
int get hashCode => Object.hash(runtimeType, failureOrTable, isUpdating);
/// Create a copy of TableFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$TableFormStateImplCopyWith<_$TableFormStateImpl> get copyWith =>
__$$TableFormStateImplCopyWithImpl<_$TableFormStateImpl>(
this,
_$identity,
);
}
abstract class _TableFormState implements TableFormState {
factory _TableFormState({
required final Option<Either<TableFailure, Table>> failureOrTable,
final bool isUpdating,
}) = _$TableFormStateImpl;
@override
Option<Either<TableFailure, Table>> get failureOrTable;
@override
bool get isUpdating;
/// Create a copy of TableFormState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$TableFormStateImplCopyWith<_$TableFormStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,9 @@
part of 'table_form_bloc.dart';
@freezed
class TableFormEvent with _$TableFormEvent {
const factory TableFormEvent.updated({
required String id,
required Offset position,
}) = _Updated;
}

View File

@ -0,0 +1,11 @@
part of 'table_form_bloc.dart';
@freezed
class TableFormState with _$TableFormState {
factory TableFormState({
required Option<Either<TableFailure, Table>> failureOrTable,
@Default(false) bool isUpdating,
}) = _TableFormState;
factory TableFormState.initial() => TableFormState(failureOrTable: none());
}

View File

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -21,6 +23,15 @@ class TableLoaderBloc extends Bloc<TableLoaderEvent, TableLoaderState> {
Emitter<TableLoaderState> emit,
) {
return event.map(
setSelectedTable: (e) async {
if (e.table == null) {
emit(state.copyWith(selectedTable: null));
} else {
if (e.table?.status.isAvailable ?? false) {
emit(state.copyWith(selectedTable: e.table));
}
}
},
fetched: (e) async {
var newState = state;
@ -32,6 +43,19 @@ class TableLoaderBloc extends Bloc<TableLoaderEvent, TableLoaderState> {
newState = await _mapFetchedToState(newState, isRefresh: e.isRefresh);
emit(newState);
},
updatedPostion: (e) async {
final updatedTables = state.tables.map((table) {
if (table.id == e.id) {
return table.copyWith(
positionX: e.position.dx,
positionY: e.position.dy,
);
}
return table;
}).toList();
emit(state.copyWith(tables: updatedTables));
},
);
}

View File

@ -17,39 +17,44 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$TableLoaderEvent {
bool get isRefresh => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(bool isRefresh) fetched,
required TResult Function(String id, Offset position) updatedPostion,
required TResult Function(Table? table) setSelectedTable,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(bool isRefresh)? fetched,
TResult? Function(String id, Offset position)? updatedPostion,
TResult? Function(Table? table)? setSelectedTable,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(bool isRefresh)? fetched,
TResult Function(String id, Offset position)? updatedPostion,
TResult Function(Table? table)? setSelectedTable,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
required TResult Function(_UpdatedPosition value) updatedPostion,
required TResult Function(_SetSelectedTable value) setSelectedTable,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
TResult? Function(_UpdatedPosition value)? updatedPostion,
TResult? Function(_SetSelectedTable value)? setSelectedTable,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
TResult Function(_UpdatedPosition value)? updatedPostion,
TResult Function(_SetSelectedTable value)? setSelectedTable,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TableLoaderEventCopyWith<TableLoaderEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
@ -58,8 +63,6 @@ abstract class $TableLoaderEventCopyWith<$Res> {
TableLoaderEvent value,
$Res Function(TableLoaderEvent) then,
) = _$TableLoaderEventCopyWithImpl<$Res, TableLoaderEvent>;
@useResult
$Res call({bool isRefresh});
}
/// @nodoc
@ -74,29 +77,14 @@ class _$TableLoaderEventCopyWithImpl<$Res, $Val extends TableLoaderEvent>
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? isRefresh = null}) {
return _then(
_value.copyWith(
isRefresh: null == isRefresh
? _value.isRefresh
: isRefresh // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$FetchedImplCopyWith<$Res>
implements $TableLoaderEventCopyWith<$Res> {
abstract class _$$FetchedImplCopyWith<$Res> {
factory _$$FetchedImplCopyWith(
_$FetchedImpl value,
$Res Function(_$FetchedImpl) then,
) = __$$FetchedImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool isRefresh});
}
@ -164,6 +152,8 @@ class _$FetchedImpl implements _Fetched {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(bool isRefresh) fetched,
required TResult Function(String id, Offset position) updatedPostion,
required TResult Function(Table? table) setSelectedTable,
}) {
return fetched(isRefresh);
}
@ -172,6 +162,8 @@ class _$FetchedImpl implements _Fetched {
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(bool isRefresh)? fetched,
TResult? Function(String id, Offset position)? updatedPostion,
TResult? Function(Table? table)? setSelectedTable,
}) {
return fetched?.call(isRefresh);
}
@ -180,6 +172,8 @@ class _$FetchedImpl implements _Fetched {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(bool isRefresh)? fetched,
TResult Function(String id, Offset position)? updatedPostion,
TResult Function(Table? table)? setSelectedTable,
required TResult orElse(),
}) {
if (fetched != null) {
@ -192,6 +186,8 @@ class _$FetchedImpl implements _Fetched {
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
required TResult Function(_UpdatedPosition value) updatedPostion,
required TResult Function(_SetSelectedTable value) setSelectedTable,
}) {
return fetched(this);
}
@ -200,6 +196,8 @@ class _$FetchedImpl implements _Fetched {
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
TResult? Function(_UpdatedPosition value)? updatedPostion,
TResult? Function(_SetSelectedTable value)? setSelectedTable,
}) {
return fetched?.call(this);
}
@ -208,6 +206,8 @@ class _$FetchedImpl implements _Fetched {
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
TResult Function(_UpdatedPosition value)? updatedPostion,
TResult Function(_SetSelectedTable value)? setSelectedTable,
required TResult orElse(),
}) {
if (fetched != null) {
@ -220,21 +220,350 @@ class _$FetchedImpl implements _Fetched {
abstract class _Fetched implements TableLoaderEvent {
const factory _Fetched({final bool isRefresh}) = _$FetchedImpl;
@override
bool get isRefresh;
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$UpdatedPositionImplCopyWith<$Res> {
factory _$$UpdatedPositionImplCopyWith(
_$UpdatedPositionImpl value,
$Res Function(_$UpdatedPositionImpl) then,
) = __$$UpdatedPositionImplCopyWithImpl<$Res>;
@useResult
$Res call({String id, Offset position});
}
/// @nodoc
class __$$UpdatedPositionImplCopyWithImpl<$Res>
extends _$TableLoaderEventCopyWithImpl<$Res, _$UpdatedPositionImpl>
implements _$$UpdatedPositionImplCopyWith<$Res> {
__$$UpdatedPositionImplCopyWithImpl(
_$UpdatedPositionImpl _value,
$Res Function(_$UpdatedPositionImpl) _then,
) : super(_value, _then);
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? id = null, Object? position = null}) {
return _then(
_$UpdatedPositionImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
position: null == position
? _value.position
: position // ignore: cast_nullable_to_non_nullable
as Offset,
),
);
}
}
/// @nodoc
class _$UpdatedPositionImpl implements _UpdatedPosition {
const _$UpdatedPositionImpl({required this.id, required this.position});
@override
final String id;
@override
final Offset position;
@override
String toString() {
return 'TableLoaderEvent.updatedPostion(id: $id, position: $position)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$UpdatedPositionImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.position, position) ||
other.position == position));
}
@override
int get hashCode => Object.hash(runtimeType, id, position);
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$UpdatedPositionImplCopyWith<_$UpdatedPositionImpl> get copyWith =>
__$$UpdatedPositionImplCopyWithImpl<_$UpdatedPositionImpl>(
this,
_$identity,
);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(bool isRefresh) fetched,
required TResult Function(String id, Offset position) updatedPostion,
required TResult Function(Table? table) setSelectedTable,
}) {
return updatedPostion(id, position);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(bool isRefresh)? fetched,
TResult? Function(String id, Offset position)? updatedPostion,
TResult? Function(Table? table)? setSelectedTable,
}) {
return updatedPostion?.call(id, position);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(bool isRefresh)? fetched,
TResult Function(String id, Offset position)? updatedPostion,
TResult Function(Table? table)? setSelectedTable,
required TResult orElse(),
}) {
if (updatedPostion != null) {
return updatedPostion(id, position);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
required TResult Function(_UpdatedPosition value) updatedPostion,
required TResult Function(_SetSelectedTable value) setSelectedTable,
}) {
return updatedPostion(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
TResult? Function(_UpdatedPosition value)? updatedPostion,
TResult? Function(_SetSelectedTable value)? setSelectedTable,
}) {
return updatedPostion?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
TResult Function(_UpdatedPosition value)? updatedPostion,
TResult Function(_SetSelectedTable value)? setSelectedTable,
required TResult orElse(),
}) {
if (updatedPostion != null) {
return updatedPostion(this);
}
return orElse();
}
}
abstract class _UpdatedPosition implements TableLoaderEvent {
const factory _UpdatedPosition({
required final String id,
required final Offset position,
}) = _$UpdatedPositionImpl;
String get id;
Offset get position;
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$UpdatedPositionImplCopyWith<_$UpdatedPositionImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$SetSelectedTableImplCopyWith<$Res> {
factory _$$SetSelectedTableImplCopyWith(
_$SetSelectedTableImpl value,
$Res Function(_$SetSelectedTableImpl) then,
) = __$$SetSelectedTableImplCopyWithImpl<$Res>;
@useResult
$Res call({Table? table});
$TableCopyWith<$Res>? get table;
}
/// @nodoc
class __$$SetSelectedTableImplCopyWithImpl<$Res>
extends _$TableLoaderEventCopyWithImpl<$Res, _$SetSelectedTableImpl>
implements _$$SetSelectedTableImplCopyWith<$Res> {
__$$SetSelectedTableImplCopyWithImpl(
_$SetSelectedTableImpl _value,
$Res Function(_$SetSelectedTableImpl) _then,
) : super(_value, _then);
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? table = freezed}) {
return _then(
_$SetSelectedTableImpl(
freezed == table
? _value.table
: table // ignore: cast_nullable_to_non_nullable
as Table?,
),
);
}
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TableCopyWith<$Res>? get table {
if (_value.table == null) {
return null;
}
return $TableCopyWith<$Res>(_value.table!, (value) {
return _then(_value.copyWith(table: value));
});
}
}
/// @nodoc
class _$SetSelectedTableImpl implements _SetSelectedTable {
const _$SetSelectedTableImpl(this.table);
@override
final Table? table;
@override
String toString() {
return 'TableLoaderEvent.setSelectedTable(table: $table)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SetSelectedTableImpl &&
(identical(other.table, table) || other.table == table));
}
@override
int get hashCode => Object.hash(runtimeType, table);
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SetSelectedTableImplCopyWith<_$SetSelectedTableImpl> get copyWith =>
__$$SetSelectedTableImplCopyWithImpl<_$SetSelectedTableImpl>(
this,
_$identity,
);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(bool isRefresh) fetched,
required TResult Function(String id, Offset position) updatedPostion,
required TResult Function(Table? table) setSelectedTable,
}) {
return setSelectedTable(table);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(bool isRefresh)? fetched,
TResult? Function(String id, Offset position)? updatedPostion,
TResult? Function(Table? table)? setSelectedTable,
}) {
return setSelectedTable?.call(table);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(bool isRefresh)? fetched,
TResult Function(String id, Offset position)? updatedPostion,
TResult Function(Table? table)? setSelectedTable,
required TResult orElse(),
}) {
if (setSelectedTable != null) {
return setSelectedTable(table);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
required TResult Function(_UpdatedPosition value) updatedPostion,
required TResult Function(_SetSelectedTable value) setSelectedTable,
}) {
return setSelectedTable(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
TResult? Function(_UpdatedPosition value)? updatedPostion,
TResult? Function(_SetSelectedTable value)? setSelectedTable,
}) {
return setSelectedTable?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
TResult Function(_UpdatedPosition value)? updatedPostion,
TResult Function(_SetSelectedTable value)? setSelectedTable,
required TResult orElse(),
}) {
if (setSelectedTable != null) {
return setSelectedTable(this);
}
return orElse();
}
}
abstract class _SetSelectedTable implements TableLoaderEvent {
const factory _SetSelectedTable(final Table? table) = _$SetSelectedTableImpl;
Table? get table;
/// Create a copy of TableLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SetSelectedTableImplCopyWith<_$SetSelectedTableImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$TableLoaderState {
List<Table> get tables => throw _privateConstructorUsedError;
Option<TableFailure> get failureOption => throw _privateConstructorUsedError;
Table? get selectedTable => throw _privateConstructorUsedError;
bool get isFetching => throw _privateConstructorUsedError;
bool get hasReachedMax => throw _privateConstructorUsedError;
int get page => throw _privateConstructorUsedError;
@ -256,10 +585,13 @@ abstract class $TableLoaderStateCopyWith<$Res> {
$Res call({
List<Table> tables,
Option<TableFailure> failureOption,
Table? selectedTable,
bool isFetching,
bool hasReachedMax,
int page,
});
$TableCopyWith<$Res>? get selectedTable;
}
/// @nodoc
@ -279,6 +611,7 @@ class _$TableLoaderStateCopyWithImpl<$Res, $Val extends TableLoaderState>
$Res call({
Object? tables = null,
Object? failureOption = null,
Object? selectedTable = freezed,
Object? isFetching = null,
Object? hasReachedMax = null,
Object? page = null,
@ -293,6 +626,10 @@ class _$TableLoaderStateCopyWithImpl<$Res, $Val extends TableLoaderState>
? _value.failureOption
: failureOption // ignore: cast_nullable_to_non_nullable
as Option<TableFailure>,
selectedTable: freezed == selectedTable
? _value.selectedTable
: selectedTable // ignore: cast_nullable_to_non_nullable
as Table?,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
@ -309,6 +646,20 @@ class _$TableLoaderStateCopyWithImpl<$Res, $Val extends TableLoaderState>
as $Val,
);
}
/// Create a copy of TableLoaderState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TableCopyWith<$Res>? get selectedTable {
if (_value.selectedTable == null) {
return null;
}
return $TableCopyWith<$Res>(_value.selectedTable!, (value) {
return _then(_value.copyWith(selectedTable: value) as $Val);
});
}
}
/// @nodoc
@ -323,10 +674,14 @@ abstract class _$$TableLoaderStateImplCopyWith<$Res>
$Res call({
List<Table> tables,
Option<TableFailure> failureOption,
Table? selectedTable,
bool isFetching,
bool hasReachedMax,
int page,
});
@override
$TableCopyWith<$Res>? get selectedTable;
}
/// @nodoc
@ -345,6 +700,7 @@ class __$$TableLoaderStateImplCopyWithImpl<$Res>
$Res call({
Object? tables = null,
Object? failureOption = null,
Object? selectedTable = freezed,
Object? isFetching = null,
Object? hasReachedMax = null,
Object? page = null,
@ -359,6 +715,10 @@ class __$$TableLoaderStateImplCopyWithImpl<$Res>
? _value.failureOption
: failureOption // ignore: cast_nullable_to_non_nullable
as Option<TableFailure>,
selectedTable: freezed == selectedTable
? _value.selectedTable
: selectedTable // ignore: cast_nullable_to_non_nullable
as Table?,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
@ -382,6 +742,7 @@ class _$TableLoaderStateImpl implements _TableLoaderState {
_$TableLoaderStateImpl({
required final List<Table> tables,
required this.failureOption,
this.selectedTable,
this.isFetching = false,
this.hasReachedMax = false,
this.page = 1,
@ -398,6 +759,8 @@ class _$TableLoaderStateImpl implements _TableLoaderState {
@override
final Option<TableFailure> failureOption;
@override
final Table? selectedTable;
@override
@JsonKey()
final bool isFetching;
@override
@ -409,7 +772,7 @@ class _$TableLoaderStateImpl implements _TableLoaderState {
@override
String toString() {
return 'TableLoaderState(tables: $tables, failureOption: $failureOption, isFetching: $isFetching, hasReachedMax: $hasReachedMax, page: $page)';
return 'TableLoaderState(tables: $tables, failureOption: $failureOption, selectedTable: $selectedTable, isFetching: $isFetching, hasReachedMax: $hasReachedMax, page: $page)';
}
@override
@ -420,6 +783,8 @@ class _$TableLoaderStateImpl implements _TableLoaderState {
const DeepCollectionEquality().equals(other._tables, _tables) &&
(identical(other.failureOption, failureOption) ||
other.failureOption == failureOption) &&
(identical(other.selectedTable, selectedTable) ||
other.selectedTable == selectedTable) &&
(identical(other.isFetching, isFetching) ||
other.isFetching == isFetching) &&
(identical(other.hasReachedMax, hasReachedMax) ||
@ -432,6 +797,7 @@ class _$TableLoaderStateImpl implements _TableLoaderState {
runtimeType,
const DeepCollectionEquality().hash(_tables),
failureOption,
selectedTable,
isFetching,
hasReachedMax,
page,
@ -453,6 +819,7 @@ abstract class _TableLoaderState implements TableLoaderState {
factory _TableLoaderState({
required final List<Table> tables,
required final Option<TableFailure> failureOption,
final Table? selectedTable,
final bool isFetching,
final bool hasReachedMax,
final int page,
@ -463,6 +830,8 @@ abstract class _TableLoaderState implements TableLoaderState {
@override
Option<TableFailure> get failureOption;
@override
Table? get selectedTable;
@override
bool get isFetching;
@override
bool get hasReachedMax;

View File

@ -4,4 +4,10 @@ part of 'table_loader_bloc.dart';
class TableLoaderEvent with _$TableLoaderEvent {
const factory TableLoaderEvent.fetched({@Default(false) bool isRefresh}) =
_Fetched;
const factory TableLoaderEvent.updatedPostion({
required String id,
required Offset position,
}) = _UpdatedPosition;
const factory TableLoaderEvent.setSelectedTable(Table? table) =
_SetSelectedTable;
}

View File

@ -5,6 +5,7 @@ class TableLoaderState with _$TableLoaderState {
factory TableLoaderState({
required List<Table> tables,
required Option<TableFailure> failureOption,
Table? selectedTable,
@Default(false) bool isFetching,
@Default(false) bool hasReachedMax,
@Default(1) int page,

View File

@ -5,4 +5,9 @@ abstract class ITableRepository {
int page = 1,
int limit = 50,
});
Future<Either<TableFailure, Table>> updatePosition({
required String id,
required Offset position,
});
}

View File

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

View File

@ -1,6 +1,7 @@
import 'dart:developer';
import 'package:data_channel/data_channel.dart';
import 'package:flutter/widgets.dart';
import 'package:injectable/injectable.dart';
import '../../../common/api/api_client.dart';
@ -42,4 +43,32 @@ class TableRemoteDataProvider {
return DC.error(TableFailure.serverError(e));
}
}
Future<DC<TableFailure, TableDto>> updatePosition({
required String id,
required Offset position,
}) async {
try {
final response = await _apiClient.put(
'${ApiPath.tables}/$id',
data: {
'position_x': position.dx.round(),
'position_y': position.dy.round(),
},
headers: getAuthorizationHeader(),
);
if (response.data['success'] == false) {
return DC.error(TableFailure.unexpectedError());
}
final table = TableDto.fromJson(
response.data['data'] as Map<String, dynamic>,
);
return DC.data(table);
} on ApiFailure catch (e, s) {
log('updatePositionTableError', name: _logName, error: e, stackTrace: s);
return DC.error(TableFailure.serverError(e));
}
}
}

View File

@ -1,4 +1,5 @@
import 'dart:developer';
import 'dart:ui';
import 'package:dartz/dartz.dart';
import 'package:injectable/injectable.dart';
@ -36,4 +37,28 @@ class TableRepository implements ITableRepository {
return left(const TableFailure.unexpectedError());
}
}
@override
Future<Either<TableFailure, Table>> updatePosition({
required String id,
required Offset position,
}) async {
try {
final result = await _remoteDataProvider.updatePosition(
id: id,
position: position,
);
if (result.hasError) {
return left(result.error!);
}
final table = result.data!.toDomain();
return right(table);
} catch (e) {
log('updatePositionTableError', name: _logName, error: e);
return left(const TableFailure.unexpectedError());
}
}
}

View File

@ -21,6 +21,8 @@ import 'package:apskel_pos_flutter_v2/application/outlet/outlet_loader/outlet_lo
import 'package:apskel_pos_flutter_v2/application/product/product_loader/product_loader_bloc.dart'
as _i13;
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;
import 'package:apskel_pos_flutter_v2/application/table/table_loader/table_loader_bloc.dart'
as _i424;
import 'package:apskel_pos_flutter_v2/common/api/api_client.dart' as _i457;
@ -189,6 +191,9 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i424.TableLoaderBloc>(
() => _i424.TableLoaderBloc(gh<_i983.ITableRepository>()),
);
gh.factory<_i248.TableFormBloc>(
() => _i248.TableFormBloc(gh<_i983.ITableRepository>()),
);
return this;
}
}

View File

@ -2,12 +2,14 @@ import 'package:auto_route/auto_route.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 '../../../../../injection.dart';
import '../../../../components/loader/loader_with_text.dart';
import 'widgets/floating_bottom_navbar.dart';
import 'widgets/table_card.dart';
@RoutePage()
@ -18,16 +20,20 @@ class TablePage extends StatefulWidget implements AutoRouteWrapper {
State<TablePage> createState() => _TablePageState();
@override
Widget wrappedRoute(BuildContext context) => BlocProvider(
create: (context) =>
getIt<TableLoaderBloc>()
..add(TableLoaderEvent.fetched(isRefresh: true)),
Widget wrappedRoute(BuildContext context) => MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>
getIt<TableLoaderBloc>()
..add(TableLoaderEvent.fetched(isRefresh: true)),
),
BlocProvider(create: (context) => getIt<TableFormBloc>()),
],
child: this,
);
}
class _TablePageState extends State<TablePage> {
t.Table? selectedTable;
t.Table? draggingTable;
Offset? tapPosition;
@ -134,7 +140,7 @@ class _TablePageState extends State<TablePage> {
),
),
...state.tables.map((table) {
final isSelected = selectedTable == table;
final isSelected = state.selectedTable == table;
return Positioned(
left: table.positionX,
top: table.positionY,
@ -165,42 +171,53 @@ class _TablePageState extends State<TablePage> {
});
},
onDragEnd: (details) {
final RenderBox box =
context.findRenderObject()
as RenderBox;
final Offset local = box.globalToLocal(
details.offset,
);
final newX = local.dx.clamp(
0.0,
mapWidth - 120,
);
final newY = local.dy.clamp(
0.0,
mapHeight - 80,
);
setState(() {
draggingTable = null;
final RenderBox box =
context.findRenderObject()
as RenderBox;
final Offset local = box.globalToLocal(
details.offset,
);
table = table.copyWith(
positionX: local.dx.clamp(
0,
mapWidth - 120,
),
positionY: local.dy.clamp(
0,
mapHeight - 80,
),
);
});
// context.read<ChangePositionTableBloc>().add(
// ChangePositionTableEvent.changePositionTable(
// tableId: table.id ?? "",
// position: Offset(
// table.positionX ?? 0.0,
// table.positionY ?? 0.0,
// ),
// ),
// );
// Update position locally in bloc state
context.read<TableLoaderBloc>().add(
TableLoaderEvent.updatedPostion(
id: table.id,
position: Offset(newX, newY),
),
);
context.read<TableFormBloc>().add(
TableFormEvent.updated(
id: table.id,
position: Offset(newX, newY),
),
);
},
child: GestureDetector(
onTap: () {},
onTap: () {
context.read<TableLoaderBloc>().add(
TableLoaderEvent.setSelectedTable(
table,
),
);
},
onTapDown: (details) =>
tapPosition = details.globalPosition,
onLongPress: () {
if (table.status.isOccupied) {
// _showPopupMenu(context, table);
_showPopupMenu(context, table);
}
},
child: TableCard(
@ -218,6 +235,7 @@ class _TablePageState extends State<TablePage> {
),
],
),
TableFloatingBottomNavbar(selectedTable: state.selectedTable),
],
),
);
@ -235,4 +253,39 @@ class _TablePageState extends State<TablePage> {
],
);
}
void _showPopupMenu(BuildContext context, t.Table table) {
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
showMenu(
context: context,
position: RelativeRect.fromRect(
tapPosition != null
? Rect.fromLTWH(tapPosition!.dx, tapPosition!.dy, 0, 0)
: Rect.fromLTWH(100, 100, 0, 0),
Offset.zero & overlay.size,
),
color: AppColor.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
elevation: 1,
items: [
PopupMenuItem(
onTap: () {
// showDialog(
// context: context,
// builder: (context) => TransferTableDialog(fromTable: table),
// );
},
child: Row(
children: [
Icon(Icons.swap_horiz),
SizedBox(width: 8),
Text('Transfer'),
],
),
),
],
);
}
}

View File

@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../../application/table/table_loader/table_loader_bloc.dart';
import '../../../../../../common/theme/theme.dart';
import '../../../../../../domain/table/table.dart' as t;
class TableFloatingBottomNavbar extends StatelessWidget {
final t.Table? selectedTable;
const TableFloatingBottomNavbar({super.key, this.selectedTable});
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 20, // Jarak dari bawah
left: 16, // Jarak dari kiri
right: 16,
child: AnimatedSlide(
duration: const Duration(milliseconds: 400),
curve: Curves.elasticOut,
offset: selectedTable == null ? const Offset(0, 2) : Offset.zero,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 200),
opacity: selectedTable == null ? 0.0 : 1.0,
child: IgnorePointer(
ignoring: selectedTable == null,
child: Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColor.primary, AppColor.primary.withOpacity(0.6)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: AppColor.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Row(
children: [
const Icon(Icons.table_bar, color: Colors.white, size: 24),
const SizedBox(width: 12),
Text(
"1 Meja Dipilih",
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(width: 16),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
selectedTable?.tableName ?? "",
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 4),
GestureDetector(
onTap: () {
context.read<TableLoaderBloc>().add(
TableLoaderEvent.setSelectedTable(null),
);
},
child: const Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
],
),
),
),
],
),
),
),
const SizedBox(width: 16),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: AppColor.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
onPressed: () {
if (selectedTable?.status.isAvailable ?? false) {
// context.pushReplacement(
// DashboardPage(
// table: selectedTable!,
// items: widget.items,
// ),
// );
} else {}
},
child: const Text(
"Tempatkan Pesanan",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
),
);
}
}