feat: transfer table

This commit is contained in:
efrilm
2025-08-08 14:22:12 +07:00
parent 94725964ea
commit f05347c90d
7 changed files with 1389 additions and 138 deletions
+191 -138
View File
@@ -1,3 +1,4 @@
import 'package:enaklo_pos/core/components/flushbar.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
@@ -5,7 +6,9 @@ import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
import 'package:enaklo_pos/presentation/table/blocs/change_position_table/change_position_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/create_table/create_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/blocs/transfer_table/transfer_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/dialogs/form_table_new_dialog.dart';
import 'package:enaklo_pos/presentation/table/dialogs/transfer_table_dialog.dart';
import 'package:enaklo_pos/presentation/table/widgets/table_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -41,6 +44,8 @@ class _TablePageState extends State<TablePage> {
// Untuk drag
TableModel? draggingTable;
Offset? _tapPosition;
// Ubah function toggleSelectTable menjadi selectTable
void selectTable(TableModel table) {
if (table.status == 'occupied') return;
@@ -112,141 +117,164 @@ class _TablePageState extends State<TablePage> {
),
),
backgroundColor: const Color(0xFFF7F8FA),
body: BlocBuilder<GetTableBloc, GetTableState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => SizedBox.shrink(),
loading: () => const Center(child: CircularProgressIndicator()),
success: (tables) => SafeArea(
child: Stack(
children: [
// Main content area
Row(
children: [
// Area meja (zoom & pan & drag)
Expanded(
flex: 5,
child: InteractiveViewer(
panEnabled: true,
scaleEnabled: true,
constrained: false,
boundaryMargin: const EdgeInsets.all(80),
minScale: 0.3,
maxScale: 3.0,
alignment: Alignment.topLeft,
child: Container(
width: mapWidth,
height: mapHeight,
decoration: BoxDecoration(
color: const Color(0xFFF7F8FA),
border: Border.all(
color: Colors.grey[300]!, width: 2),
),
child: Stack(
children: [
// Optional: Grid background
...List.generate(
20,
(i) => Positioned(
left: i * 100.0,
top: 0,
bottom: 0,
child: Container(
width: 1,
color: Colors.grey[200]),
)),
...List.generate(
15,
(i) => Positioned(
top: i * 100.0,
left: 0,
right: 0,
child: Container(
height: 1,
color: Colors.grey[200]),
)),
body: BlocListener<TransferTableBloc, TransferTableState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: () {
AppFlushbar.showSuccess(context, 'Transfer Table Success');
context.read<GetTableBloc>().add(const GetTableEvent.getTables());
},
error: (message) {
AppFlushbar.showError(context, message);
},
);
},
child: BlocBuilder<GetTableBloc, GetTableState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => SizedBox.shrink(),
loading: () => const Center(child: CircularProgressIndicator()),
success: (tables) => SafeArea(
child: Stack(
children: [
// Main content area
Row(
children: [
// Area meja (zoom & pan & drag)
Expanded(
flex: 5,
child: InteractiveViewer(
panEnabled: true,
scaleEnabled: true,
constrained: false,
boundaryMargin: const EdgeInsets.all(80),
minScale: 0.3,
maxScale: 3.0,
alignment: Alignment.topLeft,
child: Container(
width: mapWidth,
height: mapHeight,
decoration: BoxDecoration(
color: const Color(0xFFF7F8FA),
border: Border.all(
color: Colors.grey[300]!, width: 2),
),
child: Stack(
children: [
// Optional: Grid background
...List.generate(
20,
(i) => Positioned(
left: i * 100.0,
top: 0,
bottom: 0,
child: Container(
width: 1,
color: Colors.grey[200]),
)),
...List.generate(
15,
(i) => Positioned(
top: i * 100.0,
left: 0,
right: 0,
child: Container(
height: 1,
color: Colors.grey[200]),
)),
// Tables
...tables.map((table) {
final isSelected = selectedTable == table;
return Positioned(
left: table.positionX,
top: table.positionY,
child: Draggable<TableModel>(
data: table,
feedback: Material(
color: Colors.transparent,
child: TableWidget(
table: table,
isSelected: isSelected,
// Tables
...tables.map((table) {
final isSelected = selectedTable == table;
return Positioned(
left: table.positionX,
top: table.positionY,
child: Draggable<TableModel>(
data: table,
feedback: Material(
color: Colors.transparent,
child: TableWidget(
table: table,
isSelected: isSelected,
),
),
childWhenDragging: Opacity(
opacity: 0.5,
child: TableWidget(
table: table,
isSelected: isSelected,
),
),
onDragStarted: () {
setState(() {
draggingTable = table;
});
},
onDraggableCanceled:
(velocity, offset) {
setState(() {
draggingTable = null;
});
},
onDragEnd: (details) {
setState(() {
draggingTable = null;
final RenderBox box =
context.findRenderObject()
as RenderBox;
final Offset local = box
.globalToLocal(details.offset);
table.positionX = local.dx
.clamp(0, mapWidth - 120);
table.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,
),
));
},
child: GestureDetector(
onTap: () => selectTable(table),
onTapDown: (details) => _tapPosition =
details.globalPosition,
onLongPress: () {
if (table.status == 'occupied') {
_showPopupMenu(context, table);
}
},
child: TableWidget(
table: table,
isSelected: isSelected,
),
),
),
childWhenDragging: Opacity(
opacity: 0.5,
child: TableWidget(
table: table,
isSelected: isSelected,
),
),
onDragStarted: () {
setState(() {
draggingTable = table;
});
},
onDraggableCanceled: (velocity, offset) {
setState(() {
draggingTable = null;
});
},
onDragEnd: (details) {
setState(() {
draggingTable = null;
final RenderBox box = context
.findRenderObject() as RenderBox;
final Offset local =
box.globalToLocal(details.offset);
table.positionX =
local.dx.clamp(0, mapWidth - 120);
table.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,
),
));
},
child: GestureDetector(
onTap: () => selectTable(table),
child: TableWidget(
table: table,
isSelected: isSelected,
),
),
),
);
}).toList(),
],
);
}).toList(),
],
),
),
),
),
),
// Sidebar bar tables
],
),
// Sidebar bar tables
],
),
// Floating bottom bar - hanya muncul jika ada table yang dipilih
buildAlternativeFloatingBar(),
],
// Floating bottom bar - hanya muncul jika ada table yang dipilih
buildAlternativeFloatingBar(),
],
),
),
),
);
},
);
},
),
),
);
}
@@ -366,17 +394,7 @@ class _TablePageState extends State<TablePage> {
context.push(DashboardPage(
table: selectedTable!,
));
} else {
// Handle occupied table click - load draft order and navigate to payment
// context.read<CheckoutBloc>().add(
// CheckoutEvent.loadDraftOrder(data!),
// );
// log("Data Draft Order: ${data!.toMap()}");
// context.push(PaymentTablePage(
// table: widget.table,
// draftOrder: data!,
// ));
}
} else {}
},
child: const Text(
"Tempatkan Pesanan",
@@ -401,4 +419,39 @@ class _TablePageState extends State<TablePage> {
],
);
}
void _showPopupMenu(BuildContext context, TableModel 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: AppColors.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'),
],
),
),
],
);
}
}