From a04eb703f5885063dd23bad95fc1111bbc2227d6 Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Sat, 29 Apr 2023 02:32:38 +0700 Subject: [PATCH] Table creation implemented --- lib/models/table_column_definition.dart | 88 +++++++ lib/pages/dialogs/create_table_dialog.dart | 251 +++++++++++++++++++ lib/pages/home_panels/tables_list_panel.dart | 8 +- 3 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 lib/models/table_column_definition.dart create mode 100644 lib/pages/dialogs/create_table_dialog.dart diff --git a/lib/models/table_column_definition.dart b/lib/models/table_column_definition.dart new file mode 100644 index 0000000..c1d2975 --- /dev/null +++ b/lib/models/table_column_definition.dart @@ -0,0 +1,88 @@ +abstract class TableColumnDefinition { + final String columnName; + final bool isUnique; + + TableColumnDefinition({ + required this.columnName, + required this.isUnique, + }); + + String get def => throw UnimplementedError("def getter not implemented"); +} + +class SerialPrimaryColumnDefinition extends TableColumnDefinition { + SerialPrimaryColumnDefinition({ + required super.columnName, + }) : super(isUnique: true); + + @override + String get def => "$columnName:serial:primary"; +} + +class TextColumnDefinition extends TableColumnDefinition { + TextColumnDefinition({ + required super.columnName, + required super.isUnique, + }); + + @override + String get def => "$columnName:str${isUnique ? ":unique" : ""}"; +} + +class BooleanColumnDefinition extends TableColumnDefinition { + BooleanColumnDefinition({ + required super.columnName, + required super.isUnique, + }); + + @override + String get def => "$columnName:bool${isUnique ? ":unique" : ""}"; +} + +class TimestampColumnDefinition extends TableColumnDefinition { + TimestampColumnDefinition({ + required super.columnName, + required super.isUnique, + }); + + @override + String get def => "$columnName:datetime${isUnique ? ":unique" : ""}"; +} + +class DoubleColumnDefinition extends TableColumnDefinition { + DoubleColumnDefinition({ + required super.columnName, + required super.isUnique, + }); + + @override + String get def => "$columnName:float${isUnique ? ":unique" : ""}"; +} + +class IntegerColumnDefinition extends TableColumnDefinition { + IntegerColumnDefinition({ + required super.columnName, + required super.isUnique, + }); + + @override + String get def => "$columnName:int${isUnique ? ":unique" : ""}"; +} + +class UserRefColumnDefinition extends TableColumnDefinition { + UserRefColumnDefinition({ + required super.columnName, + }) : super(isUnique: false); + + @override + String get def => "$columnName:int-user"; +} + +class AssetRefColumnDefinition extends TableColumnDefinition { + AssetRefColumnDefinition({ + required super.columnName, + }) : super(isUnique: false); + + @override + String get def => "$columnName:int-asset"; +} diff --git a/lib/pages/dialogs/create_table_dialog.dart b/lib/pages/dialogs/create_table_dialog.dart new file mode 100644 index 0000000..7d9e014 --- /dev/null +++ b/lib/pages/dialogs/create_table_dialog.dart @@ -0,0 +1,251 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_fast_forms/flutter_fast_forms.dart'; +import 'package:get/get.dart'; +import 'package:styled_widget/styled_widget.dart'; +import 'package:tuuli_app/api_controller.dart'; +import 'package:tuuli_app/models/table_column_definition.dart'; + +const typeToNameMatcher = { + SerialPrimaryColumnDefinition: "Serial ID", + TextColumnDefinition: "Text", + BooleanColumnDefinition: "Boolean", + TimestampColumnDefinition: "Date/Time", + DoubleColumnDefinition: "Double", + IntegerColumnDefinition: "Integer", + UserRefColumnDefinition: "User reference", + AssetRefColumnDefinition: "Asset reference", +}; + +class CreateTableController extends GetxController { + final _tableName = "".obs; + String get tableName => _tableName.value; + set tableName(String value) => _tableName.value = value; + + final _columnsDefinition = [].obs; + List get columnsDefinition => _columnsDefinition; + + Future createTable() async { + try { + final resp = await ApiController.to.apiClient.createTable( + tableName: tableName, + columnsDefinition: + _columnsDefinition.map((e) => e.def).toList(growable: false), + ); + + final respData = resp.data; + if (respData == null) { + throw Exception("No data in response"); + } + + Get.back(result: true); + } on DioError catch (e) { + final respData = e.response?.data; + if (respData != null) { + Get.snackbar( + "Error trying to update tables access", + "${respData['error']}", + ); + } else { + Get.snackbar( + "Error trying to update tables access", + "$e", + ); + } + } catch (e) { + Get.snackbar( + "Error trying to update tables access", + "$e", + ); + } + } + + Future createNewColumn() async { + final columnName = "".obs; + final columnType = "".obs; + final columnIsUnique = false.obs; + + final confirm = await Get.dialog( + AlertDialog( + title: const Text("Create new column"), + content: Wrap( + runSpacing: 16, + children: [ + FastTextField( + name: "ColumnName", + decoration: const InputDecoration( + labelText: "Column name", + border: OutlineInputBorder(), + ), + initialValue: columnName.value, + onChanged: (value) => columnName.value = value ?? "", + ), + FastDropdown( + name: "ColumnTypes", + hint: const Text("Select column type"), + items: typeToNameMatcher.keys.toList(growable: false), + itemsBuilder: (items, field) => items + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(typeToNameMatcher[e]!), + ), + ) + .toList(), + onChanged: (value) => + columnType.value = typeToNameMatcher[value] ?? "", + ), + FastCheckbox( + name: "ColumnIsUnique", + titleText: "Is Unique", + initialValue: false, + onChanged: (value) => columnIsUnique.value = value!, + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Get.back(result: false); + }, + child: const Text('Close'), + ), + TextButton( + onPressed: () { + Get.back(result: true); + }, + child: const Text('Create'), + ), + ], + ), + ); + + if (confirm != true) return; + + TableColumnDefinition? ct; + switch (columnType.value) { + case "Serial ID": + ct = SerialPrimaryColumnDefinition( + columnName: columnName.value, + ); + break; + case "Text": + ct = TextColumnDefinition( + columnName: columnName.value, + isUnique: columnIsUnique.value, + ); + break; + case "Boolean": + ct = BooleanColumnDefinition( + columnName: columnName.value, + isUnique: columnIsUnique.value, + ); + break; + case "Date/Time": + ct = TimestampColumnDefinition( + columnName: columnName.value, + isUnique: columnIsUnique.value, + ); + break; + case "Double": + ct = DoubleColumnDefinition( + columnName: columnName.value, + isUnique: columnIsUnique.value, + ); + break; + case "Integer": + ct = IntegerColumnDefinition( + columnName: columnName.value, + isUnique: columnIsUnique.value, + ); + break; + case "User reference": + ct = UserRefColumnDefinition( + columnName: columnName.value, + ); + break; + case "Asset reference": + ct = AssetRefColumnDefinition( + columnName: columnName.value, + ); + break; + } + if (ct == null) return; + _columnsDefinition.add(ct); + } + + void removeColumn(TableColumnDefinition e) { + _columnsDefinition.remove(e); + } +} + +class CreateTableDialog extends GetView { + const CreateTableDialog({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Creating new table'), + content: Column( + children: [ + TextField( + decoration: const InputDecoration( + labelText: "Table name", + border: OutlineInputBorder(), + ), + onChanged: (value) => controller.tableName = value, + ), + const Divider(), + Obx( + () => ListView( + children: controller.columnsDefinition + .map( + (e) => ListTile( + title: Text(e.columnName), + subtitle: Text(e.def), + trailing: IconButton( + onPressed: () => controller.removeColumn(e), + icon: const Icon(Icons.delete), + ), + ), + ) + .toList(), + ).expanded(), + ), + const Divider(), + [ + ElevatedButton( + onPressed: () => controller.createNewColumn(), + child: const Text("Create column"), + ).expanded() + ].toRow(), + ], + ).constrained(width: Get.width * 0.9, height: Get.height * 0.9), + actions: [ + TextButton( + onPressed: () { + Get.back(); + }, + child: const Text('Close'), + ), + Obx( + () => TextButton( + onPressed: controller.columnsDefinition.isEmpty + ? null + : () => controller.createTable(), + child: const Text('Create'), + ), + ), + ], + ); + } + + static Future show() async { + Get.lazyPut(() => CreateTableController()); + + return Get.dialog( + const CreateTableDialog(), + barrierDismissible: false, + ); + } +} diff --git a/lib/pages/home_panels/tables_list_panel.dart b/lib/pages/home_panels/tables_list_panel.dart index 66e70e9..ddc6770 100644 --- a/lib/pages/home_panels/tables_list_panel.dart +++ b/lib/pages/home_panels/tables_list_panel.dart @@ -5,6 +5,7 @@ import 'package:tuuli_api/tuuli_api.dart'; import 'package:tuuli_app/api_controller.dart'; import 'package:recase/recase.dart'; import 'package:tuuli_app/models/db_column_definition.dart'; +import 'package:tuuli_app/pages/dialogs/create_table_dialog.dart'; class TablesListPanelController extends GetxController { @override @@ -54,7 +55,12 @@ class TablesListPanelController extends GetxController { _isLoading.value = false; } - Future createNewTable() async {} + Future createNewTable() async { + final created = await CreateTableDialog.show(); + if (created == true) { + refreshData(); + } + } Future openTable(TableDefinition table) async {} }