update
This commit is contained in:
@@ -1,17 +1,238 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_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/table_card.dart';
|
||||
|
||||
@RoutePage()
|
||||
class TablePage extends StatelessWidget {
|
||||
class TablePage extends StatefulWidget implements AutoRouteWrapper {
|
||||
const TablePage({super.key});
|
||||
|
||||
@override
|
||||
State<TablePage> createState() => _TablePageState();
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(BuildContext context) => BlocProvider(
|
||||
create: (context) =>
|
||||
getIt<TableLoaderBloc>()
|
||||
..add(TableLoaderEvent.fetched(isRefresh: true)),
|
||||
child: this,
|
||||
);
|
||||
}
|
||||
|
||||
class _TablePageState extends State<TablePage> {
|
||||
t.Table? selectedTable;
|
||||
t.Table? draggingTable;
|
||||
|
||||
Offset? tapPosition;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Table Page',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
final double mapWidth = context.deviceWidth * 2;
|
||||
final double mapHeight = context.deviceHeight * 1.5;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
appBar: AppBar(
|
||||
title: const Text("Layout Meja"),
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
elevation: 0.5,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
context.read<TableLoaderBloc>().add(
|
||||
const TableLoaderEvent.fetched(isRefresh: true),
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) => FormTableNewDialog(),
|
||||
// );
|
||||
},
|
||||
),
|
||||
],
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(20),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
_buildLegendDot(Colors.blue[200]!, "Available"),
|
||||
const SizedBox(width: 16),
|
||||
_buildLegendDot(Colors.orange[200]!, "Occupied"),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: BlocBuilder<TableLoaderBloc, TableLoaderState>(
|
||||
builder: (context, state) {
|
||||
if (state.isFetching) {
|
||||
return const Center(child: LoaderWithText());
|
||||
}
|
||||
return SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
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: [
|
||||
...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],
|
||||
),
|
||||
),
|
||||
),
|
||||
...state.tables.map((table) {
|
||||
final isSelected = selectedTable == table;
|
||||
return Positioned(
|
||||
left: table.positionX,
|
||||
top: table.positionY,
|
||||
child: Draggable<t.Table>(
|
||||
data: table,
|
||||
feedback: Material(
|
||||
color: Colors.transparent,
|
||||
child: TableCard(
|
||||
table: table,
|
||||
isSelected: isSelected,
|
||||
),
|
||||
),
|
||||
childWhenDragging: Opacity(
|
||||
opacity: 0.5,
|
||||
child: TableCard(
|
||||
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 = 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,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {},
|
||||
onTapDown: (details) =>
|
||||
tapPosition = details.globalPosition,
|
||||
onLongPress: () {
|
||||
if (table.status.isOccupied) {
|
||||
// _showPopupMenu(context, table);
|
||||
}
|
||||
},
|
||||
child: TableCard(
|
||||
table: table,
|
||||
isSelected: isSelected,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLegendDot(Color color, String label) {
|
||||
return Row(
|
||||
children: [
|
||||
CircleAvatar(radius: 7, backgroundColor: color),
|
||||
const SizedBox(width: 6),
|
||||
Text(label, style: const TextStyle(fontSize: 14)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../../common/function/app_function.dart';
|
||||
import '../../../../../../common/theme/theme.dart';
|
||||
import '../../../../../../domain/table/table.dart' as t;
|
||||
import '../../../../../../domain/table/table.dart';
|
||||
|
||||
class TableCard extends StatelessWidget {
|
||||
final t.Table table;
|
||||
final bool isSelected;
|
||||
const TableCard({super.key, required this.table, required this.isSelected});
|
||||
|
||||
Color getStatusColor() {
|
||||
switch (table.status) {
|
||||
case TableStatusType.available:
|
||||
return Colors.blue[100]!;
|
||||
case TableStatusType.occupied:
|
||||
return Colors.orange[100]!;
|
||||
default:
|
||||
return Colors.grey[200]!;
|
||||
}
|
||||
}
|
||||
|
||||
Color getBorderColor() {
|
||||
if (isSelected) return AppColor.primary;
|
||||
switch (table.status) {
|
||||
case TableStatusType.available:
|
||||
return Colors.blue;
|
||||
case TableStatusType.occupied:
|
||||
return Colors.orange;
|
||||
default:
|
||||
return Colors.grey;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final int capacity = table.capacity;
|
||||
final chairDist = getChairDistribution(capacity);
|
||||
|
||||
Widget chair() => Container(
|
||||
width: 20,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
width: table.capacity > 16
|
||||
? 240
|
||||
: table.capacity > 8
|
||||
? 180
|
||||
: 120,
|
||||
height: 80,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: table.capacity > 16
|
||||
? 220
|
||||
: table.capacity > 8
|
||||
? 160
|
||||
: 100,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(color: getBorderColor(), width: 2),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Center(
|
||||
child: CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: getStatusColor(),
|
||||
child: Text(
|
||||
table.tableName,
|
||||
style: TextStyle(
|
||||
color: getBorderColor(),
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (chairDist['top']! > 0)
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 10,
|
||||
right: 10,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(chairDist['top']!, (_) => chair()),
|
||||
),
|
||||
),
|
||||
// Kursi bawah
|
||||
if (chairDist['bottom']! > 0)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 10,
|
||||
right: 10,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(chairDist['bottom']!, (_) => chair()),
|
||||
),
|
||||
),
|
||||
// Kursi kiri
|
||||
if (chairDist['left']! > 0)
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 15,
|
||||
bottom: 15,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(chairDist['left']!, (_) => chair()),
|
||||
),
|
||||
),
|
||||
// Kursi kanan
|
||||
if (chairDist['right']! > 0)
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 15,
|
||||
bottom: 15,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(chairDist['right']!, (_) => chair()),
|
||||
),
|
||||
),
|
||||
// Icon info kecil di pojok kanan atas jika status reserved
|
||||
if (table.status.isOccupied)
|
||||
const Positioned(
|
||||
top: 6,
|
||||
right: 6,
|
||||
child: Icon(
|
||||
Icons.info_outline,
|
||||
size: 16,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ class TableRoute extends _i10.PageRouteInfo<void> {
|
||||
static _i10.PageInfo page = _i10.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i9.TablePage();
|
||||
return _i10.WrappedRoute(child: const _i9.TablePage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user