tuuli_app/lib/pages/dialogs/group_acl_dialog.dart

409 lines
12 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:data_table_2/data_table_2.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart' hide Response;
import 'package:recase/recase.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:tuuli_api/tuuli_api.dart';
import 'package:tuuli_app/api_controller.dart';
import 'package:tuuli_app/models/group_definition.dart';
import 'package:tuuli_app/models/table_access.dart';
class GroupACLController extends GetxController {
final GroupDefinition group;
GroupACLController(this.group);
@override
void onInit() {
super.onInit();
refreshData();
}
final _tables = <TableDefinition>[].obs;
List<TableDefinition> get tables => _tables.toList();
final _access = <String, TableAccess>{}.obs;
Map<String, TableAccess> get access => _access;
final _allowedColumns = <String, String?>{}.obs;
Map<String, String?> get allowedColumns => _allowedColumns;
Future<void> refreshData() async {
await refreshTables();
for (final table in tables) {
_access[table.tableId] = TableAccess.none;
}
for (final table in tables) {
await refreshTableAccess(table);
}
}
Future<void> refreshTables() async {
try {
final resp = await ApiController.to.apiClient.listTables();
final respData = resp.data;
if (respData == null) {
throw Exception("В ответе нет данных");
}
_tables.clear();
_tables.addAll(respData.where((e) => e.hidden != true));
} on DioError catch (e) {
final respData = e.response?.data;
if (respData != null) {
Get.snackbar(
"Ошибка запроса таблиц",
"${respData['error']}",
);
} else {
Get.snackbar(
"Ошибка запроса таблиц",
"$e",
);
}
} catch (e) {
Get.snackbar(
"Ошибка запроса таблиц",
"$e",
);
}
}
Future<void> refreshTableAccess(TableDefinition table) async {
try {
final resp = await ApiController.to.apiClient.getItemsFromTable(
tableName: "table_access",
itemsSelector: ItemsSelector(
fields: ["access_type", "allowed_columns"],
where: [
ColumnConditionCompat(
column: "user_group_id",
operator_: ColumnConditionCompatOperator.eq,
value: group.id,
),
ColumnConditionCompat(
column: "table_name",
operator_: ColumnConditionCompatOperator.eq,
value: table.tableName,
),
],
),
);
final respData = resp.data;
if (respData == null) {
throw Exception("В ответе нет данных");
}
if (respData.isNotEmpty) {
_access[table.tableId] =
TableAccess.fromString(respData.first["access_type"]);
_allowedColumns[table.tableId] = respData.first["allowed_columns"];
} else {
_access[table.tableId] = TableAccess.none;
_allowedColumns[table.tableId] = null;
}
} on DioError catch (e) {
final respData = e.response?.data;
if (respData != null) {
Get.snackbar(
"Ошибка запроса информации доступа к таблицам",
"${respData['error']}",
);
} else {
Get.snackbar(
"Ошибка запроса информации доступа к таблицам",
"$e",
);
}
} catch (e) {
Get.snackbar(
"Ошибка запроса информации доступа к таблицам",
"$e",
);
}
}
Future<void> updateTableAccess(
TableDefinition table, {
required bool read,
required bool write,
}) async {
final oldTableAccess = _access[table.tableId];
var newTableAccess = TableAccess.none;
if (read && write) {
newTableAccess = TableAccess.readWrite;
} else if (read) {
newTableAccess = TableAccess.read;
} else if (write) {
newTableAccess = TableAccess.write;
}
try {
Response<OkResponse> resp;
if (newTableAccess == TableAccess.none) {
resp = await ApiController.to.apiClient.deleteItemFromTable(
tableName: "table_access",
columnConditionCompat: [
ColumnConditionCompat(
column: "user_group_id",
operator_: ColumnConditionCompatOperator.eq,
value: group.id,
),
ColumnConditionCompat(
column: "table_name",
operator_: ColumnConditionCompatOperator.eq,
value: table.tableName,
),
],
);
} else if (oldTableAccess == TableAccess.none) {
resp = await ApiController.to.apiClient.createItem(
tableName: "table_access",
itemDefinition: {
"user_group_id": group.id,
"table_name": table.tableName,
"access_type": newTableAccess.def,
},
);
} else {
resp = await ApiController.to.apiClient.updateItemInTable(
tableName: "table_access",
itemUpdate: ItemUpdate(
item: {
"access_type": newTableAccess.def,
},
oldItem: {
"user_group_id": group.id,
"table_name": table.tableName,
},
),
);
}
final respData = resp.data;
if (respData == null) {
throw Exception("В ответе нет данных");
}
refreshTableAccess(table);
} on DioError catch (e) {
final respData = e.response?.data;
if (respData != null) {
Get.snackbar(
"Ошибка обновления информации доступа к таблицам",
"${respData['error']}",
);
} else {
Get.snackbar(
"Ошибка обновления информации доступа к таблицам",
"$e",
);
}
} catch (e) {
Get.snackbar(
"Ошибка обновления информации доступа к таблицам",
"$e",
);
}
}
Future<void> changeAllowedColumns(TableDefinition table) async {
final tableColumns = table.columns
.split(",")
.map((e) => e.split(":").first)
.where((e) => e.isNotEmpty)
.toList();
final currentlyAvailableColumns =
_allowedColumns[table.tableId]!.split(",");
if (currentlyAvailableColumns.length == 1 &&
currentlyAvailableColumns.first == "*") {
currentlyAvailableColumns.clear();
currentlyAvailableColumns.addAll(tableColumns);
}
final selectedColumns = <String, bool>{}.obs;
for (final column in tableColumns) {
selectedColumns[column] = currentlyAvailableColumns.contains(column);
}
final confirm = await Get.dialog<bool>(
AlertDialog(
title: const Text("Разрешённые колонки"),
content: Obx(
() => Wrap(
children: [
ElevatedButton(
onPressed: () {
for (final column in tableColumns) {
selectedColumns[column] = !selectedColumns[column]!;
}
},
child: const Text("Перевернуть"),
),
...selectedColumns.entries.map((e) {
return CheckboxListTile(
title: Text(e.key),
value: e.value,
onChanged: (value) => selectedColumns[e.key] = value!,
);
}),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(result: false),
child: const Text("Отменить"),
),
TextButton(
onPressed: () => Get.back(result: true),
child: const Text("Ок"),
),
],
),
);
if (confirm != true) return;
if (selectedColumns.values.every((e) => !e)) {
await Get.dialog(
AlertDialog(
title: const Text("Ошибка"),
content: const Text("Необходимо выбрать хотя бы одну колонку"),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text("Ок"),
),
],
),
);
return;
}
try {
Response<OkResponse> resp =
await ApiController.to.apiClient.updateItemInTable(
tableName: "table_access",
itemUpdate: ItemUpdate(
item: {
"allowed_columns": selectedColumns.keys
.where((k) => selectedColumns[k]!)
.join(","),
},
oldItem: {
"user_group_id": group.id,
"table_name": table.tableName,
},
),
);
final respData = resp.data;
if (respData == null) {
throw Exception("В ответе нет данных");
}
refreshTableAccess(table);
} on DioError catch (e) {
final respData = e.response?.data;
if (respData != null) {
Get.snackbar(
"Ошибка обновления информации доступа к таблицам",
"${respData['error']}",
);
} else {
Get.snackbar(
"Ошибка обновления информации доступа к таблицам",
"$e",
);
}
} catch (e) {
Get.snackbar(
"Ошибка обновления информации доступа к таблицам",
"$e",
);
}
}
}
class GroupACLDialog extends GetView<GroupACLController> {
const GroupACLDialog({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Доступ группы'),
content: Obx(
() => DataTable2(
columns: const [
DataColumn2(label: Text('Таблица'), size: ColumnSize.L),
DataColumn2(label: Text('Чтение'), size: ColumnSize.S),
DataColumn2(label: Text('Запись'), size: ColumnSize.S),
DataColumn2(label: Text('Колонки'), size: ColumnSize.S),
],
empty: const Text("Нет таблиц"),
rows: controller.access.entries.map((e) {
final table = controller.tables.firstWhere(
(element) => element.tableId == e.key,
);
final tableAccess = e.value;
final read = tableAccess == TableAccess.read ||
tableAccess == TableAccess.readWrite;
final write = tableAccess == TableAccess.write ||
tableAccess == TableAccess.readWrite;
return DataRow(cells: [
DataCell(Text(table.tableName.pascalCase)),
DataCell(Checkbox(
value: read,
onChanged: (value) => controller.updateTableAccess(
table,
read: value ?? false,
write: write,
),
)),
DataCell(Checkbox(
value: write,
onChanged: (value) => controller.updateTableAccess(
table,
read: read,
write: value ?? false,
),
)),
controller.allowedColumns[table.tableId] == null
? DataCell.empty
: DataCell(
Text(controller.allowedColumns[table.tableId]!),
onTap: () => controller.changeAllowedColumns(table),
),
]);
}).toList(growable: false),
).constrained(width: Get.width * 0.9, height: Get.height * 0.9),
),
actions: [
TextButton(
onPressed: () {
Get.back();
},
child: const Text("Закрыть"),
),
],
);
}
static Future<void> show(GroupDefinition group) async {
Get.lazyPut<GroupACLController>(() => GroupACLController(group));
await Get.dialog(
const GroupACLDialog(),
barrierDismissible: false,
);
Get.delete<GroupACLController>();
}
}