import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:data_table_2/data_table_2.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:recase/recase.dart'; import 'package:tuuli_app/api/api_client.dart'; import 'package:tuuli_app/api/model/tables_list_model.dart'; import 'package:tuuli_app/pages/bottomsheets/create_table_item_bottomsheet.dart'; class OpenTableBottomSheet extends StatefulWidget { final TableModel table; const OpenTableBottomSheet({super.key, required this.table}); @override State createState() => _OpenTableBottomSheetState(); } class _OpenTableBottomSheetState extends State { final apiClient = Get.find(); final tableItems = TableItemsDataList.empty(growable: true); @override void initState() { super.initState(); _refreshTableData(); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16), child: Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Text( widget.table.tableName.pascalCase, style: Theme.of(context).textTheme.headlineSmall, ), const Spacer(), IconButton( onPressed: _addNewItem, icon: const Icon(Icons.add), ), IconButton( onPressed: _refreshTableData, icon: const Icon(Icons.refresh), ), IconButton( onPressed: _dropTable, icon: const Icon(Icons.delete), ), IconButton( onPressed: Get.back, icon: const Icon(Icons.cancel), ), ], ), const Divider(), Expanded( child: DataTable2( columnSpacing: 12, horizontalMargin: 12, headingRowColor: MaterialStateColor.resolveWith((states) => Colors.black), columns: [ ...widget.table.columns.map((e) => DataColumn( label: Text(e.fieldName), )), const DataColumn(label: Text("Actions")), ], rows: tableItems .map((e) => DataRow(cells: [ for (int i = 0; i < widget.table.columns.length; i++) DataCell( Text(e[widget.table.columns[i].fieldName] ?.toString() ?? "null"), ), DataCell( Row( children: [ IconButton( onPressed: () => _deleteItem(e), icon: const Icon(Icons.delete), ), IconButton( onPressed: () => _updateExistingItem(e), icon: const Icon(Icons.edit), ), ], ), ), ])) .toList(growable: false), empty: const Center(child: Text("No data")), ), ), ], ), ), ); } Future _dropTable() async { final really = await Get.defaultDialog( title: "Drop table", middleText: "Are you sure you want to drop this table \"${widget.table.tableName}\"?", textConfirm: "Drop", onConfirm: () => Get.back(result: true), onCancel: () {}, barrierDismissible: false, ); if (really != true) { return; } final result = await apiClient.dropTable(widget.table.tableName); result.unfold((data) { Get.back(); }, (error) { Get.snackbar("Error", error.toString()); }); } Future _refreshTableData() async { final result = await apiClient.getTableItems(widget.table); result.unfold((data) { setState(() { tableItems.clear(); tableItems.addAll(data); }); }, (error) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Error: $error"), ), ); }); } Future _addNewItem() async { final newItem = await showFlexibleBottomSheet>( minHeight: 1, initHeight: 1, maxHeight: 1, context: context, builder: (_, __, ___) => CreateTableItemBottomSheet(table: widget.table), anchors: [0, 0.5, 1], isSafeArea: true, isDismissible: false, ); if (newItem == null) { return; } final result = await apiClient.insertItem(widget.table, newItem); result.unfold((data) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Item added"), ), ); _refreshTableData(); }, (error) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Error: $error"), ), ); }); } Future _updateExistingItem(TableItemsData oldItem) async { final newItem = await showFlexibleBottomSheet>( minHeight: 1, initHeight: 1, maxHeight: 1, context: context, builder: (_, __, ___) => CreateTableItemBottomSheet( table: widget.table, existingItem: Map.fromEntries(widget.table.columns .where((el) => !el.isPrimary) .map((el) => MapEntry(el.fieldName, oldItem[el.fieldName]))), ), anchors: [0, 0.5, 1], isSafeArea: true, isDismissible: false, ); if (newItem == null) { return; } final result = await apiClient.updateItem(widget.table, newItem, oldItem); result.unfold((data) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Item added"), ), ); _refreshTableData(); }, (error) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Error: $error"), ), ); }); } Future _deleteItem(TableItemsData e) async { final really = await Get.defaultDialog( title: "Delete item", middleText: "Are you sure you want to delete this item?", textConfirm: "Delete", onConfirm: () => Get.back(result: true), onCancel: () {}, barrierDismissible: false, ); if (really != true) { return; } final result = await apiClient.deleteItem(widget.table, e); result.unfold((data) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Item deleted"), ), ); _refreshTableData(); }, (error) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Error: $error"), ), ); }); } }