]> gitweb.hamatoma.de Git - exhibition.git/commitdiff
I18N, Validators, Benchmarks, Scopes, Users
authorHamatoma <author.hamatoma.de>
Sat, 29 Jan 2022 19:16:50 +0000 (20:16 +0100)
committerHamatoma <author.hamatoma.de>
Sat, 29 Jan 2022 19:17:55 +0000 (20:17 +0100)
* I18N: I18N(): fix: null test
* validators:
** new: isTime()
** fix: isInt(), isNat(): message if input is null
* Benchmarks>:
** random_data: warnings removed
* Structures changed to Scopes
* Users: new attribute status
* GlobalWidgets: new comboUsers()
* new: assets/
* new: validator_test

55 files changed:
assets/white-16x16.png [new file with mode: 0644]
dart_tools/bin/i18n_text_parser.dart
lib/base/i18n.dart
lib/base/validators.dart
lib/common/module_meta_data.dart [new file with mode: 0644]
lib/common/random_data.dart
lib/meta/modules.dart
lib/meta/scopes_meta.dart [new file with mode: 0644]
lib/meta/structures_meta.dart [deleted file]
lib/meta/users_meta.dart
lib/page/benchmarks/benchmark_data.dart
lib/page/benchmarks/create_benchmark_page.dart
lib/page/benchmarks/delete_benchmark_page.dart
lib/page/benchmarks/edit_benchmark_page.dart
lib/page/benchmarks/list_benchmark_page.dart
lib/page/page_manager.dart
lib/page/roles/create_role_page.dart
lib/page/roles/edit_role_page.dart
lib/page/roles/list_role_page.dart
lib/page/roles/role_data.dart
lib/page/rolestarter/list_rolestarter_page.dart
lib/page/rolestarter/mapping_rolestarter_page.dart
lib/page/rolestarter/role_starter_data.dart
lib/page/scopes/create_scope_custom.dart [new file with mode: 0644]
lib/page/scopes/create_scope_page.dart [new file with mode: 0644]
lib/page/scopes/delete_scope_custom.dart [new file with mode: 0644]
lib/page/scopes/delete_scope_page.dart [new file with mode: 0644]
lib/page/scopes/edit_scope_custom.dart [new file with mode: 0644]
lib/page/scopes/edit_scope_page.dart [new file with mode: 0644]
lib/page/scopes/list_scope_custom.dart [new file with mode: 0644]
lib/page/scopes/list_scope_page.dart [new file with mode: 0644]
lib/page/scopes/scope_data.dart [new file with mode: 0644]
lib/page/starters/create_starter_page.dart
lib/page/starters/delete_starter_page.dart
lib/page/starters/edit_starter_page.dart
lib/page/starters/list_starter_page.dart
lib/page/starters/starter_data.dart
lib/page/structures/create_structure_custom.dart [deleted file]
lib/page/structures/create_structure_page.dart [deleted file]
lib/page/structures/delete_structure_custom.dart [deleted file]
lib/page/structures/delete_structure_page.dart [deleted file]
lib/page/structures/edit_structure_custom.dart [deleted file]
lib/page/structures/edit_structure_page.dart [deleted file]
lib/page/structures/list_structure_custom.dart [deleted file]
lib/page/structures/list_structure_page.dart [deleted file]
lib/page/structures/structure_data.dart [deleted file]
lib/page/users/create_user_page.dart
lib/page/users/delete_user_page.dart
lib/page/users/edit_user_page.dart
lib/page/users/list_user_page.dart
lib/page/users/user_data.dart
lib/services/global_widget.dart
metatool/Compile
test/i18n_text_parser_test.dart
test/validators_test.dart [new file with mode: 0644]

diff --git a/assets/white-16x16.png b/assets/white-16x16.png
new file mode 100644 (file)
index 0000000..11d70f9
Binary files /dev/null and b/assets/white-16x16.png differ
index bcfca05c6e0192c14042274aea426b26efd8387e..c89607bc2bac36e502de84d086f8881fb1af9b58 100644 (file)
@@ -103,11 +103,13 @@ class I18nTextParser {
   List<String> lines = [];
   int currentLineNo = 0;
   int currentIxLines = 0;
-  late RegExp regExpExcluded;
+  RegExp regExpExcluded = RegExp('^(Never°DEFined)\$');
+
   // ...........................1.............1.........2
   final regExpModule =
       RegExp(r'''(\w+) = (I18N\(\)|i18n)\.module\(["'](.*?)['"]\);''');
   final regExpDelimiter = RegExp(r'''["']''');
+
   // ..........1.............1..2..............................2
   final regExpText = RegExp(r'(i18n|I18N\(\))\.(tr|ntr|trMulti|trArgs)\(');
   final regExpStringConstant = RegExp(r'''^\s*(r?)(["'])(.*?)\2''');
index 528f67f6ad05f7e99e88f01a5d37a083c302b10e..503b9d933bfcb7b5f605db818d2da7d956851364 100644 (file)
@@ -12,10 +12,19 @@ class I18N {
   String locale = 'de_DE';
   String loadedLocale = '';
   final regExpTag = RegExp(r'<#\d+>$');
+
   factory I18N() {
+    if (instance == null) {
+      instance = I18N.internal(MemoryLogger());
+    }
     return instance!;
   }
 
+  /**
+   * Constructor for initializing the global instance.
+   *
+   * @param logger: the logger used for error messages.
+   */
   I18N.internal(this.logger) {
     instance = this;
   }
index ad862af0939fa03f59058704a8e8425f82f3df34..97bae759d2a1ac9353f50a7c578051d24f248759 100644 (file)
@@ -1,3 +1,5 @@
+import 'package:dart_bones/dart_bones.dart';
+
 import 'i18n.dart';
 
 final i18n = I18N();
@@ -6,14 +8,22 @@ final _regExprEMailChar = RegExp(r'[\/ "!$%&()?;:,*<>|^°{}\[\]\\=]');
 
 final _regExprEMailFormat = RegExp(r'^[^@]+@[^@]+\.[a-zA-Z]+$');
 
+final _regExprTime = RegExp(r'^(\d{1,2})(:(\d{1,2}))?$');
+
+final _regExprNat = RegExp(r'^\d+$');
+
 /// Tests whether [input] is an correct email address.
 ///
 /// Returns null on success, the error message otherwise.
 String? isEmail(String? input) {
   String? rc;
-  if (input != null) {
+  if (input == null) {
+    rc = i18n.tr('null is not an email');
+  } else {
     RegExpMatch? match = _regExprEMailChar.firstMatch(input);
-    if (match != null) {
+    if (countChar(input, '@') > 1) {
+      rc = i18n.tr('too many "@" in email address: ') + input;
+    } else if (match != null) {
       rc = i18n.trArgs('Illegal character "{0}" in email address', '!global',
           [match.group(0)!]);
     } else if (_regExprEMailFormat.firstMatch(input) == null) {
@@ -26,7 +36,9 @@ String? isEmail(String? input) {
 /// Tests whether the [input] is a not negative integer.
 String? isInt(String? input) {
   String? rc;
-  if (input != null) {
+  if (input == null) {
+    rc = i18n.tr('null is not an integer');
+  } else {
     final value = int.tryParse(input);
     if (value == null) {
       rc = i18n.trArgs('Not an integer: {0}', '!global', [input]);
@@ -38,11 +50,11 @@ String? isInt(String? input) {
 /// Tests whether the [input] is a not negative integer.
 String? isNat(String? input) {
   String? rc;
-  if (input != null) {
-    final value = int.tryParse(input);
-    if (value == null) {
-      rc = i18n.trArgs('Not an integer: {0}', '!global', [input]);
-    } else if (value < 0) {
+  if (input == null) {
+    rc = i18n.tr('null is not a not negative integer');
+  } else {
+    RegExpMatch? match = _regExprNat.firstMatch(input);
+    if (match == null) {
       rc = i18n.trArgs(
           'Not negative integer expected, not: {0}', '!global', [input]);
     }
@@ -50,6 +62,30 @@ String? isNat(String? input) {
   return rc;
 }
 
+/// Tests whether the [input] is a time, e.g. "22:44".
+String? isTime(String? input, {bool mayBeEmpty = false}) {
+  String? rc;
+  RegExpMatch? match;
+  if (input == null || input.isEmpty) {
+    if (!mayBeEmpty) {
+      rc = i18n.tr('Nothing is not a time');
+    }
+  } else {
+    if ((match = _regExprTime.firstMatch(input)) == null) {
+      rc = i18n
+          .trArgs('Not a time: {0} Examples: 10 or 9:44 ', '!global', [input]);
+    } else if (int.parse(match!.group(1)!) >= 24) {
+      rc = i18n.trArgs('Wrong hour in: {0}: 0..23', '!global', [input]);
+    } else if (match.groupCount >= 3 &&
+        match.group(3) != null &&
+        int.parse(match.group(3)!) >= 60) {
+      rc = i18n
+          .trArgs('Wrong minutes in: {0} Expected 0..59', '!global', [input]);
+    }
+  }
+  return rc;
+}
+
 /// Tests whether the input is not empty.
 String? notEmpty(String? input) {
   final rc = input == null || input.isEmpty ? i18n.tr('Please fill in.') : null;
diff --git a/lib/common/module_meta_data.dart b/lib/common/module_meta_data.dart
new file mode 100644 (file)
index 0000000..692208b
--- /dev/null
@@ -0,0 +1,491 @@
+import 'package:dart_bones/dart_bones.dart';
+
+import '../base/defines.dart';
+
+typedef MetaMenuItemBuilder = List<dynamic> Function<T>(
+    PropertyMetaData propertyMetaData);
+
+/// Describes a button of a page.
+class ButtonMetaData extends WidgetMetaData {
+  ButtonMetaData(String name) : super(name, WidgetType.button) {
+    //@ToDo
+  }
+}
+
+class CopyDbFields extends FieldMetaData {
+  CopyDbFields(String name, {String options = ''})
+      : super(name, options, dataType: DataType.undefined);
+}
+
+/// Describes a field related to a database column.
+class DbFieldMetaData extends WidgetMetaData {
+  PropertyMetaData? reference;
+
+  DbFieldMetaData(String name, String reference)
+      : super(name, WidgetType.dbField) {
+    //@ToDo
+  }
+}
+
+enum DisplayType {
+  checkbox,
+  combobox,
+  custom,
+  switchWidget,
+  text,
+}
+
+/// Describes a field of the page.
+class FieldMetaData extends WidgetMetaData {
+  DataType dataType;
+  DisplayType displayType;
+  final String options;
+
+  FieldMetaData(String name, this.options,
+      {this.dataType = DataType.string, this.displayType = DisplayType.text})
+      : super(name, WidgetType.field);
+}
+
+/// Describes the list page, that displays an overview over all records of the
+/// related table.
+class ListPageMetaData extends PageMetaData {
+  final String tableHeaders;
+  final String tableColumns;
+  final String whereCondition;
+  final String orderBy;
+  final String selectItems;
+  final String joinItems;
+  final String widgetsBelowFilter;
+  final String toolTipAddButton;
+
+  /// Constructor.
+  ///
+  /// [name] is the page name. It must be unique over all pages in the module.
+  ///
+  /// [fields]: the field in the filter section.
+  ///
+  /// [tableColumns]: a semicolon delimited list of table columns used in the
+  /// table displaying the filtered records. Example: 'user_id;user_name;role'
+  ///
+  /// [tableHeaders]: an auto delimited list of headers for the table. Example:
+  /// ';Id;Name;Role'. Auto delimited: the first char defines the delimiter.
+  ///
+  /// [globalComboBoxes]: If there are filter combo boxes that can be constructed
+  /// by "global methods" the should be listed here.
+  /// Example: 'comboRoles;comboUsers'
+  ///
+  /// [whereCondition]: the filter condition in the SQL statement. Example:
+  /// '(:text IS NULL OR user_name like :text OR user_displayname like :text)'
+  ///
+  /// [orderBy]: the default order by clause. Example: 'changed desc,user_id'
+  ///
+  /// [selectItems]: additional select entries. Must end with ','. Example:
+  /// '''(SELECT count(*) FROM sessions WHERE sessions.user_id=t0.user_id) as count,
+  /// t1.role_created as roledate,'''
+  ///
+  /// [joinItems]: additional joins. Convention: use table ids different from
+  /// the created joins (t1, t2, ...), use j1, j2 ...
+  /// Example: '''JOIN sessions j1 ON j1.user_id=t0.user_id
+  /// JOIN logins j2 ON j2.user_id=t0.user_id'''
+  ///
+  /// [widgetsBelowFilter]: additional widget
+  ListPageMetaData(String label,
+      {String name = '',
+      required List<WidgetMetaData> fields,
+      required this.tableColumns,
+      required this.tableHeaders,
+      String globalComboBoxes = '',
+      this.whereCondition = '',
+      this.orderBy = '',
+      this.selectItems = '',
+      this.joinItems = '',
+      this.widgetsBelowFilter = '',
+      this.toolTipAddButton = ''})
+      : super(label, PageType.list,
+            name: name, fields: fields, globalComboBoxes: globalComboBoxes);
+}
+
+/// Describes a mapping page, that allows assignments of many table entries
+/// of the member table to one entry of a common table.
+///
+/// Example: Common table is roles, member table is starters. Than the page
+/// allows to assign some starters (menu items) to a role.
+class MappingPageMetaData extends PageMetaData {
+  ModuleMetaData? commonModule;
+  ModuleMetaData? memberModule;
+  PropertyMetaData? commonProperty;
+  PropertyMetaData? memberProperty;
+
+  /// Constructor.
+  ///
+  /// [name] is the page name. It must be unique over all pages in the module.
+  /// Default is a name derived from the page type.
+  ///
+  /// [commonModuleName]: the name of the module containing the common entries
+  /// of the relation.
+  ///
+  /// [memberModuleName]: the name of the module containing the member entries
+  ///  that belongs to exactly one entry in the common module.
+  ///
+  /// [commonPropertyName]: the name of the property representing the common module.
+  ///
+  /// [memberPropertyName]: the name of the property representing the member module.
+  ///
+  /// Don't forget to initialize the [commonModule], [memberModule],
+  /// [commonProperty] and [memberProperty] in the overridden method [onInitialized].
+  MappingPageMetaData(String label, {String name = ''})
+      : super(label, PageType.mapping, name: name, fields: []);
+}
+
+class MetaException extends FormatException {}
+
+/// Stores the meta data of a module.
+class ModuleMetaData {
+  static final metaColumns = [
+    'createdAt',
+    'createdBy',
+    'changedAt',
+    'changedBy',
+    'deletedAt',
+    'deletedBy'
+  ];
+
+  /// The module name, e.g. users
+  String moduleName = '';
+
+  /// The singular version of the module name, e.g. 'user'
+  String moduleNameSingular = '';
+
+  /// The related database table.
+  String tableName = '';
+
+  final bool needsSqlAll;
+
+  /// If true the fields create, createdBy ... have the same ("short") column name
+  /// instead of prefix and name.
+  bool shortModifiedLabel = false;
+  List<PropertyMetaData> propertyList;
+  List<PageMetaData> pageList;
+  final Map<String, PropertyMetaData> properties = {};
+  String columnPrefix = '';
+
+  ModuleMetaData(this.moduleName, this.propertyList, this.pageList,
+      {String tableName = '',
+      String moduleNameSingular = '',
+      String columnPrefix = '',
+      this.shortModifiedLabel = false,
+      this.needsSqlAll = false}) {
+    this.tableName = tableName.isEmpty ? moduleName.toLowerCase() : tableName;
+    if (moduleNameSingular.isEmpty) {
+      if (!moduleName.endsWith('s')) {
+        moduleNameSingular = moduleName;
+      } else {
+        final length = moduleName.length - 1;
+        moduleNameSingular = moduleName.substring(0, length);
+      }
+    }
+    this.columnPrefix = columnPrefix.isNotEmpty
+        ? columnPrefix
+        : this.moduleNameSingular.toLowerCase();
+
+    for (var item in propertyList) {
+      item.module = this;
+      properties[item.name] = item;
+      if (item.columnName.isEmpty) {
+        final prefix = shortModifiedLabel && metaColumns.contains(item.name)
+            ? ''
+            : (this.columnPrefix + '_');
+        item.columnName = prefix + item.name.toLowerCase();
+      }
+    }
+    for (var item in pageList) {
+      item.module = this;
+    }
+  }
+
+  /// Returns a given [dataType] as string.
+  String dartType(DataType dataType) {
+    String rc;
+    switch (dataType) {
+      case DataType.bool:
+        rc = 'bool';
+        break;
+      case DataType.currency:
+        rc = 'int';
+        break;
+      case DataType.date:
+      case DataType.datetime:
+        rc = 'DateTime';
+        break;
+      case DataType.float:
+        rc = 'double';
+        break;
+      case DataType.int:
+      case DataType.nat:
+      case DataType.reference:
+        rc = 'int';
+        break;
+      case DataType.string:
+        rc = 'String';
+        break;
+      case DataType.undefined:
+        throw const FormatException('dartType(): data type is undefined');
+    }
+    return rc;
+  }
+
+  /// Calculates the column option of a column.
+  /// [property] specifies the meta data of the column.
+  String dbOptions(PropertyMetaData property) {
+    String rc = '';
+    String options = property.options;
+    if (options.contains(':notnull:') || options.contains('primary')) {
+      rc += ' NOT NULL';
+    }
+    if (options.contains('unique')) {
+      rc += ' UNIQUE';
+    }
+    if (property.dataType == DataType.date ||
+        property.dataType == DataType.datetime) {
+      rc += ' NULL';
+    }
+    if (options.contains('primary')) {
+      rc += ' AUTO_INCREMENT';
+    }
+    return rc.isEmpty ? '' : ' $rc';
+  }
+
+  /// Calculates the MySQL data type.
+  /// [dataType] specifies the data type.
+  /// [size] specifies the maximal size for a string type. Only relevant if
+  /// [dataType] == DataType.string.
+  String mySqlType(DataType dataType, int size) {
+    String rc;
+    switch (dataType) {
+      case DataType.bool:
+        rc = 'CHAR(1)';
+        break;
+      case DataType.date:
+        rc = 'DATE';
+        break;
+      case DataType.datetime:
+        rc = 'TIMESTAMP';
+        break;
+      case DataType.float:
+        rc = 'FLOAT';
+        break;
+      case DataType.currency:
+        rc = 'DECIMAL(12,2)';
+        break;
+      case DataType.int:
+        rc = 'INT(10)';
+        break;
+      case DataType.nat:
+      case DataType.reference:
+        rc = 'INT(10) UNSIGNED';
+        break;
+      case DataType.string:
+        size = size == 0 ? 255 : size;
+        if (size <= 255) {
+          rc = 'VARCHAR($size)';
+        } else if (size < 65535) {
+          rc = 'TEXT';
+        } else {
+          rc = 'LARGE TEXT';
+        }
+        break;
+      case DataType.undefined:
+        throw const FormatException('mySqlType(): data type is undefined');
+    }
+    return rc;
+  }
+
+  /// Will be called after the constructor.
+  ///
+  /// Override it if needed.
+  void onInitialized() {
+    for (var page in this.pageList) {
+      page.onInitialized();
+    }
+  }
+
+  /// Returns the meta data of a page given by its [name] or null if missing.
+  PageMetaData? pageByName(String name) {
+    PageMetaData? rc;
+    for (var item in pageList) {
+      if (item.name == name) {
+        rc = item;
+        break;
+      }
+    }
+    return rc;
+  }
+
+  /// Returns the list of pages with a given [pageType]. May be empty.
+  List<PageMetaData> pagesByType(PageType pageType) {
+    final rc = <PageMetaData>[];
+    for (var item in pageList) {
+      if (item.pageType == pageType) {
+        rc.add(item);
+        break;
+      }
+    }
+    return rc;
+  }
+
+  /// Returns the primary key of the relation.
+  PropertyMetaData? primaryOf() {
+    PropertyMetaData? rc;
+    for (var item in propertyList) {
+      if (item.options.contains('primary')) {
+        rc = item;
+        break;
+      }
+    }
+    return rc;
+  }
+
+  /// Returns a property given by the [columnName].
+  PropertyMetaData? propertyByColumnName(String columnName) {
+    PropertyMetaData? rc;
+    for (var field in propertyList) {
+      if (field.columnName == columnName) {
+        rc = field;
+        break;
+      }
+    }
+    return rc;
+  }
+
+  /// Returns the meta data of a property given by its [name] or null if missing.
+  PropertyMetaData? propertyByName(String name) {
+    final rc = propertyList.singleWhere((element) => element.name == name,
+        orElse: null);
+    return rc;
+  }
+
+  /// Returns the properties that are not in [metaColumns].
+  /// : if the name of the property is [included] the property is always part of
+  /// the result.
+  Iterable<PropertyMetaData> standardColumns([String included = '']) {
+    final rc = propertyList.where(
+        (item) => item.name == included || !metaColumns.contains(item.name));
+    return rc;
+  }
+}
+
+class PageMetaData {
+  String name = '';
+  final String label;
+  final PageType pageType;
+  late ModuleMetaData module;
+  final List<WidgetMetaData> fields;
+  final String globalComboBoxes;
+
+  PageMetaData(this.label, this.pageType,
+      {this.name = '', required this.fields, this.globalComboBoxes = ''}) {
+    if (name.isEmpty) {
+      name = enumToString(pageType);
+    }
+  }
+
+  /// Does things when the instance is inititialized.
+  ///
+  /// Must be called after the constructor.
+  void onInitialized() {
+    var newFields = <WidgetMetaData>[];
+    var toDelete = <WidgetMetaData>[];
+    for (var field in fields) {
+      if (field is CopyDbFields) {
+        String excluded = '';
+        final start = field.options.indexOf('excluded=');
+        if (start >= 0) {
+          var end = field.options.indexOf(':', start);
+          if (end < 0) {
+            end = field.options.length;
+          }
+          excluded = ' ' + field.options.substring(start, end) + ' ';
+        }
+        for (var property in module.propertyList) {
+          if (excluded.contains(" ${property.name} ")) {
+            continue;
+          }
+          if (!property.hasOption(':hidden:')) {
+            newFields.add(property);
+          }
+        }
+        toDelete.add(field);
+      }
+    }
+    for (var field in toDelete) {
+      fields.remove(field);
+    }
+    fields.addAll(newFields);
+  }
+}
+
+enum PageType { create, custom, delete, edit, list, mapping }
+
+/// Stores the meta data of a module property stored as column in a database.
+class PropertyMetaData extends WidgetMetaData {
+  late ModuleMetaData module;
+  final String label;
+
+  /// Relative width of the field in a 12 column form: 1 <= width <= 12
+  int weight = 6;
+
+  /// A colon delimited list of options, e.g. ':notnull:unique:'.
+  final String options;
+  final DisplayType displayType;
+  final DataType dataType;
+  MetaMenuItemBuilder? menuItemBuilder;
+
+  /// The size if dataType is DataType.string.
+  final int size;
+  String columnName;
+  var validators = <String>[];
+
+  /// The foreign key if dataType is DataType.reference, e.g. 'users.user_id'
+  String? foreignKey;
+
+  PropertyMetaData(String name, this.label, this.dataType, this.options,
+      {this.displayType = DisplayType.text,
+      this.columnName = '',
+      this.size = 0,
+      this.foreignKey,
+      int weight = 6,
+      List<String>? validators})
+      : super(name, WidgetType.property) {
+    this.weight = weight > 12 ? 12 : weight;
+    this.validators = validators ?? [];
+  }
+
+  /// Returns whether a given [option] is set.
+  ///
+  /// [option] is a word delimited by ':', e.g. ":notnull:"
+  ///
+  /// Returns true, if [option] is part of [options], false otherwise.
+  bool hasOption(String option) {
+    return options.contains(option);
+  }
+}
+
+class ReferenceProperty extends WidgetMetaData {
+  /// This attribute gets is real value after the construcctor of the module.
+  PropertyMetaData child = PropertyMetaData('dummy', '', DataType.bool, '');
+  final String nameChild;
+
+  ReferenceProperty({required this.nameChild})
+      : super(nameChild + 'Ref', WidgetType.referenceProperty);
+}
+
+/// Describes a widget used in the page.
+/// Base class of all other widgets.
+class WidgetMetaData {
+  final WidgetType widgetType;
+  final String name;
+
+  WidgetMetaData(this.name, this.widgetType);
+}
+
+enum WidgetType { button, field, dbField, property, referenceProperty }
index ea628d66f23637c6d2f4a8a8fb57838cb9e29a14..3abfa2827e472fac85c6345b5d75723f9af44c1f 100644 (file)
@@ -1,25 +1,25 @@
 class RandomData {
-  static final verbs = const [
+  static const verbs = [
     'go', 'read', 'eat', 'jump', 'write', 'say', 'talk', 'pray', 'love',
     'hate', 'join', 'select', 'walk', 'hear', 'type', // 15
     'cry', 'cook', 'fill', 'bide', 'paint'
   ];
-  static final adjectives = const [
+  static const adjectives = [
     'high', 'low', 'hot', 'cold', 'wide', 'small', 'pretty', 'dirty',
     'simple', 'rich', 'poor', 'full', 'empty', 'happy', 'angry', // 15
     'strong', 'wise', 'dirty', 'silly', ''
   ];
-  static final animals = const [
+  static const animals = [
     'dog', 'cat', 'mouse', 'cow', 'unicorn', 'lion', 'tiger', 'sheep', 'bull',
     'bison', 'bug', 'turtle', 'camel', 'spider', 'swan', // 15
     'bunny', 'deer', 'hare', 'fox', 'pig'
   ];
-  static final things = const [
+  static const things = [
     'door', 'chair', 'wood', 'ink', 'floor', 'bed', 'bag', 'coat',
     'house', 'palace', 'street', 'way', 'city', 'table', 'wall', // 15
     'tree', 'gras', 'sky', 'hell', 'garden'
   ];
-  static final colors = const [
+  static const colors = [
     'red',
     'blue',
     'white',
@@ -31,7 +31,7 @@ class RandomData {
     'orange',
     'pink'
   ];
-  static final firstnames = const [
+  static const firstnames = [
     'Adam',
     'Alice',
     'Bill',
index 9334a33b663bfbaab3c711df5060928293fa1b11..ea69a7228370c73bb421e6ae02c438df23ff1b3b 100644 (file)
@@ -3,10 +3,9 @@ import 'module_meta_data.dart';
 import 'benchmarks_meta.dart';
 import 'roles_meta.dart';
 import 'rolestarter_meta.dart';
+import 'scopes_meta.dart';
 import 'starters_meta.dart';
-import 'structures_meta.dart';
 import 'users_meta.dart';
-
 /// Returns the meta data of the module given by [name].
 /// Returns null if not found.
 ModuleMetaData? moduleByName(String name) {
@@ -21,12 +20,12 @@ ModuleMetaData? moduleByName(String name) {
     case 'Rolestarter':
       rc = RoleStarterMeta();
       break;
+    case 'Scopes':
+      rc = ScopesMeta();
+      break;
     case 'Starters':
       rc = StartersMeta();
       break;
-    case 'Structures':
-      rc = StructuresMeta();
-      break;
     case 'Users':
       rc = UsersMeta();
       break;
@@ -35,15 +34,14 @@ ModuleMetaData? moduleByName(String name) {
   }
   return rc;
 }
-
 /// Returns the module names as string list.
 List<String> moduleNames() {
   return [
     'Benchmarks',
     'Roles',
     'Rolestarter',
+    'Scopes',
     'Starters',
-    'Structures',
     'Users',
   ];
 }
diff --git a/lib/meta/scopes_meta.dart b/lib/meta/scopes_meta.dart
new file mode 100644 (file)
index 0000000..22d2697
--- /dev/null
@@ -0,0 +1,64 @@
+import '../base/defines.dart';
+import '../base/i18n.dart';
+import 'module_meta_data.dart';
+
+final i18n = I18N();
+final M = i18n.module("Scopes");
+
+class ScopesMeta extends ModuleMetaData {
+  static ScopesMeta instance = ScopesMeta.internal();
+
+  factory ScopesMeta() {
+    return instance;
+  }
+
+  ScopesMeta.internal()
+      : super('Scopes', [
+          PropertyMetaData('id', i18n.tr('Id'), DataType.reference, ':primary:',
+              displayType: DisplayType.combobox),
+          PropertyMetaData(
+              'scope', i18n.tr('Scope'), DataType.string, ':notnull:',
+              size: 64),
+          PropertyMetaData(
+              'name', i18n.tr('Name', M), DataType.string, ':notnull:',
+              size: 64),
+          PropertyMetaData(
+              'value', i18n.tr('Value', M), DataType.string, ':notnull:',
+              size: 32),
+          PropertyMetaData(
+              'position', i18n.tr('Position', M), DataType.int, ''),
+          PropertyMetaData('createdAt', i18n.tr('Created at'),
+              DataType.datetime, ':hidden:'),
+          PropertyMetaData(
+              'createdBy', i18n.tr('Created by'), DataType.string, ':hidden:',
+              size: 32),
+          PropertyMetaData('changedAt', i18n.tr('Changed at'),
+              DataType.datetime, ':hidden:'),
+          PropertyMetaData(
+              'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:',
+              size: 32),
+        ], [
+    PageMetaData('New Scope', PageType.create,
+              fields: [CopyDbFields('fields')]),
+    PageMetaData('Change Scope', PageType.edit,
+              fields: [CopyDbFields('fields')]),
+    PageMetaData('Delete Scope', PageType.delete,
+              fields: [CopyDbFields('fields')]),
+          ListPageMetaData(
+            'Scopes Overview',
+            fields: [
+              PropertyMetaData('text', i18n.tr('Text'), DataType.string,
+                  ':where=!text IS NONE OR scope_name like !text OR scope_value like !text:',
+                  size: 64),
+            ],
+            toolTipAddButton: i18n.tr('Add a scope item'),
+            tableColumns:
+                'scope_id;scope_scope;scope_name;scope_value;scope_position',
+            tableHeaders: i18n.tr(';Id;Scope;Name;Value;Position'),
+          ),
+        ]);
+  @override
+  void onInitialized() {
+    super.onInitialized();
+  }
+}
diff --git a/lib/meta/structures_meta.dart b/lib/meta/structures_meta.dart
deleted file mode 100644 (file)
index 2f58149..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-import '../base/defines.dart';
-import '../base/i18n.dart';
-import 'module_meta_data.dart';
-
-final i18n = I18N();
-final M = i18n.module("Structures");
-
-class StructuresMeta extends ModuleMetaData {
-  static StructuresMeta instance = StructuresMeta.internal();
-  factory StructuresMeta() {
-    return instance;
-  }
-  StructuresMeta.internal()
-      : super('Structures', [
-          PropertyMetaData('id', i18n.tr('Id'), DataType.reference, ':primary:',
-              displayType: DisplayType.combobox),
-          PropertyMetaData(
-              'scope', i18n.tr('Scope'), DataType.string, ':notnull:',
-              size: 64),
-          PropertyMetaData(
-              'name', i18n.tr('Name', M), DataType.string, ':notnull:',
-              size: 64),
-          PropertyMetaData(
-              'value', i18n.tr('Value', M), DataType.string, ':notnull:',
-              size: 32),
-          PropertyMetaData(
-              'position', i18n.tr('Position', M), DataType.int, ''),
-          PropertyMetaData('createdAt', i18n.tr('Created at'),
-              DataType.datetime, ':hidden:'),
-          PropertyMetaData(
-              'createdBy', i18n.tr('Created by'), DataType.string, ':hidden:',
-              size: 32),
-          PropertyMetaData('changedAt', i18n.tr('Changed at'),
-              DataType.datetime, ':hidden:'),
-          PropertyMetaData(
-              'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:',
-              size: 32),
-        ], [
-          PageMetaData('New Structure', PageType.create,
-              fields: [CopyDbFields('fields')]),
-          PageMetaData('Change Structure', PageType.edit,
-              fields: [CopyDbFields('fields')]),
-          PageMetaData('Delete Structure', PageType.delete,
-              fields: [CopyDbFields('fields')]),
-          ListPageMetaData(
-            'Structures Overview',
-            fields: [
-              PropertyMetaData('text', i18n.tr('Text'), DataType.string,
-                  ':where=!text IS NONE OR structure_name like !text OR structure_value like !text:',
-                  size: 64),
-            ],
-            toolTipAddButton: i18n.tr('Add a structure item'),
-            tableColumns:
-                'structure_id;structure_scope;structure_name;structure_value;structure_position',
-            tableHeaders: i18n.tr(';Id;Scope;Name;Value;Position'),
-          ),
-        ]);
-  @override
-  void onInitialized() {
-    super.onInitialized();
-  }
-}
index 40c017dbfea6afd054c9daad740ab5071759fc75..db02cd4d1761a3cfdd8b09f59905f80befc9010b 100644 (file)
@@ -27,6 +27,10 @@ class UsersMeta extends ModuleMetaData {
               'role', i18n.tr('Role'), DataType.reference, ':notnull:',
               displayType: DisplayType.combobox,
               foreignKey: 'roles.role_id;role_name;role'),
+          PropertyMetaData(
+              'status', i18n.tr('Status'), DataType.reference, ':notnull:',
+              displayType: DisplayType.combobox,
+              foreignKey: 'roles.role_id;role_name;role'),
           PropertyMetaData('createdAt', i18n.tr('Created at'),
               DataType.datetime, ':hidden:'),
           PropertyMetaData(
index c693772ee77fd35075f3e136afa78526e4772c6a..9f44af663aec1768c29e63a1ef0d8a624555b390 100644 (file)
@@ -2,7 +2,6 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-
 class BenchmarkData extends DataRecord<int> {
   int? id;
   String? lastName;
@@ -16,6 +15,7 @@ class BenchmarkData extends DataRecord<int> {
   String? createdBy;
   DateTime? changedAt;
   String? changedBy;
+
   BenchmarkData(
       {this.id,
       this.lastName,
@@ -29,9 +29,11 @@ class BenchmarkData extends DataRecord<int> {
       this.createdBy,
       this.changedAt,
       this.changedBy});
+
   BenchmarkData.createFromMap(DataMap map) {
     fromMap(map);
   }
+
   @override
   void fromMap(DataMap map) {
     id = map.containsKey('benchmark_id')
@@ -126,7 +128,6 @@ class BenchmarkData extends DataRecord<int> {
     }
     return rc;
   }
-
   @override
   DataMap toMap({DataMap? map, bool clear = true}) {
     map ??= DataMap();
index 3933f0eb54603950951fa12b8b2dce2b745e88d5..a0ebe41147a48678df376781b11225c5cfc8646c 100644 (file)
@@ -28,6 +28,7 @@ class CreateBenchmarkPage extends StatefulWidget {
 
 class _CreateBenchmarkPageState extends CreateBenchmarkCustom {
   _CreateBenchmarkPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _CreateBenchmarkPageState extends CreateBenchmarkCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index e11e7011131d48725cc1525940f4359c4b9f7eb4..a016da08eca7ed89568dd7d6d935a8b5aaa867c9 100644 (file)
@@ -29,6 +29,7 @@ class DeleteBenchmarkPage extends StatefulWidget {
 
 class _DeleteBenchmarkPageState extends DeleteBenchmarkCustom {
   _DeleteBenchmarkPageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _DeleteBenchmarkPageState extends DeleteBenchmarkCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 53f305c2e6716b6f78d55762258a4a27ecb513aa..ccc3c997f18abf198edc452ed698568c4e403c6a 100644 (file)
@@ -29,6 +29,7 @@ class EditBenchmarkPage extends StatefulWidget {
 
 class _EditBenchmarkPageState extends EditBenchmarkCustom {
   _EditBenchmarkPageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _EditBenchmarkPageState extends EditBenchmarkCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 6a65fa05934b2e5890627061e831e0e940ec7d01..9074c61c9a5d177ad5b2950ee7a535a352d77144 100644 (file)
@@ -28,6 +28,7 @@ class ListBenchmarkPage extends StatefulWidget {
 
 class _ListBenchmarkPageState extends ListBenchmarkCustom {
   _ListBenchmarkPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _ListBenchmarkPageState extends ListBenchmarkCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index c4d5ff0a9311434422e81ce9c64a17203b9badab..7e0dc60e8ea32bdf5390273c5398d4719fcc8d76 100644 (file)
@@ -11,14 +11,14 @@ import 'roles/edit_role_page.dart';
 import 'roles/list_role_page.dart';
 import 'rolestarter/mapping_rolestarter_page.dart';
 import 'rolestarter/list_rolestarter_page.dart';
+import 'scopes/create_scope_page.dart';
+import 'scopes/edit_scope_page.dart';
+import 'scopes/delete_scope_page.dart';
+import 'scopes/list_scope_page.dart';
 import 'starters/create_starter_page.dart';
 import 'starters/edit_starter_page.dart';
 import 'starters/delete_starter_page.dart';
 import 'starters/list_starter_page.dart';
-import 'structures/create_structure_page.dart';
-import 'structures/edit_structure_page.dart';
-import 'structures/delete_structure_page.dart';
-import 'structures/list_structure_page.dart';
 import 'users/create_user_page.dart';
 import 'users/edit_user_page.dart';
 import 'users/delete_user_page.dart';
@@ -67,6 +67,18 @@ class PageManager {
       case '/RoleStarter/list':
         rc = ListRoleStarterPage();
         break;
+      case '/Scopes/create':
+        rc = CreateScopePage();
+        break;
+      case '/Scopes/edit':
+        rc = EditScopePage(arg1);
+        break;
+      case '/Scopes/delete':
+        rc = DeleteScopePage(arg1);
+        break;
+      case '/Scopes/list':
+        rc = ListScopePage();
+        break;
       case '/Starters/create':
         rc = CreateStarterPage();
         break;
@@ -79,18 +91,6 @@ class PageManager {
       case '/Starters/list':
         rc = ListStarterPage();
         break;
-      case '/Structures/create':
-        rc = CreateStructurePage();
-        break;
-      case '/Structures/edit':
-        rc = EditStructurePage(arg1);
-        break;
-      case '/Structures/delete':
-        rc = DeleteStructurePage(arg1);
-        break;
-      case '/Structures/list':
-        rc = ListStructurePage();
-        break;
       case '/Users/create':
         rc = CreateUserPage();
         break;
index 57426c9aebd0ff34466dbafebfff52f84db3d4c5..237ab520a1c62f7cde18115b8000ceefb321cc9c 100644 (file)
@@ -28,6 +28,7 @@ class CreateRolePage extends StatefulWidget {
 
 class _CreateRolePageState extends CreateRoleCustom {
   _CreateRolePageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _CreateRolePageState extends CreateRoleCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index c720ac87b60aa961121539324c6179168524dff4..b2abd65c5998a87ae240911311a55e738a27bac1 100644 (file)
@@ -29,6 +29,7 @@ class EditRolePage extends StatefulWidget {
 
 class _EditRolePageState extends EditRoleCustom {
   _EditRolePageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _EditRolePageState extends EditRoleCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index fee466d10761ea6c4d66c9c173a07aef321bf9b7..491a3c1dc1743273626932fafa52ed4ccc31cbef 100644 (file)
@@ -28,6 +28,7 @@ class ListRolePage extends StatefulWidget {
 
 class _ListRolePageState extends ListRoleCustom {
   _ListRolePageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _ListRolePageState extends ListRoleCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 317e9db322431a63638a7434a12f96b33d94af03..bd2c7aeef06778e5827995108b0c7bb32eff98b5 100644 (file)
@@ -2,7 +2,6 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-
 class RoleData extends DataRecord<int> {
   int? id;
   String? name;
@@ -10,6 +9,7 @@ class RoleData extends DataRecord<int> {
   String? createdBy;
   DateTime? changedAt;
   String? changedBy;
+
   RoleData(
       {this.id,
       this.name,
@@ -17,9 +17,11 @@ class RoleData extends DataRecord<int> {
       this.createdBy,
       this.changedAt,
       this.changedBy});
+
   RoleData.createFromMap(DataMap map) {
     fromMap(map);
   }
+
   @override
   void fromMap(DataMap map) {
     id = map.containsKey('role_id')
@@ -78,7 +80,6 @@ class RoleData extends DataRecord<int> {
     }
     return rc;
   }
-
   @override
   DataMap toMap({DataMap? map, bool clear = true}) {
     map ??= DataMap();
index 0d1b5b9a348630b65ee43b523e5e5f3d18313ee3..8e218ca020bcd786b93b0ef1e68d7ffcdfed56e3 100644 (file)
@@ -28,6 +28,7 @@ class ListRoleStarterPage extends StatefulWidget {
 
 class _ListRoleStarterPageState extends ListRoleStarterCustom {
   _ListRoleStarterPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _ListRoleStarterPageState extends ListRoleStarterCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 4c976807a70bb04a4b804c8afc66a9cfc9abdffb..3db5408b29bb9e39addcb08237cfed67bc5924ec 100644 (file)
@@ -28,6 +28,7 @@ class MappingRoleStarterPage extends StatefulWidget {
 
 class _MappingRoleStarterPageState extends MappingRoleStarterCustom {
   _MappingRoleStarterPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _MappingRoleStarterPageState extends MappingRoleStarterCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 2e761f3b49442f7b4780b5e6ddc1478de3ab9e0e..682fca343ab3084e9a55a199348fbb145a35f4e2 100644 (file)
@@ -2,7 +2,6 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-
 class RoleStarterData extends DataRecord<int> {
   int? id;
   int? role;
@@ -12,6 +11,7 @@ class RoleStarterData extends DataRecord<int> {
   String? createdBy;
   DateTime? changedAt;
   String? changedBy;
+
   RoleStarterData(
       {this.id,
       this.role,
@@ -21,9 +21,11 @@ class RoleStarterData extends DataRecord<int> {
       this.createdBy,
       this.changedAt,
       this.changedBy});
+
   RoleStarterData.createFromMap(DataMap map) {
     fromMap(map);
   }
+
   @override
   void fromMap(DataMap map) {
     id = map.containsKey('rolestarter_id')
@@ -94,7 +96,6 @@ class RoleStarterData extends DataRecord<int> {
     }
     return rc;
   }
-
   @override
   DataMap toMap({DataMap? map, bool clear = true}) {
     map ??= DataMap();
diff --git a/lib/page/scopes/create_scope_custom.dart b/lib/page/scopes/create_scope_custom.dart
new file mode 100644 (file)
index 0000000..7d71c17
--- /dev/null
@@ -0,0 +1,166 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../base/i18n.dart';
+import '../../base/validators.dart';
+import '../../services/global_widget.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/message_line.dart';
+import '../../widget/widget_form.dart';
+import 'create_scope_page.dart';
+
+final i18n = I18N();
+
+class CreateScopeCustom extends State<CreateScopePage> with MessageLine {
+  final globalData = GlobalData();
+  late AttendedPage attendedPage;
+  final _fieldData = _FieldData();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'CreateScope');
+  final scopeController = TextEditingController();
+  final nameController = TextEditingController();
+  final valueController = TextEditingController();
+  final positionController = TextEditingController();
+
+  CreateScopeCustom();
+
+  @override
+  Widget build(BuildContext context) {
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('New Scope')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(child: buildFrame()));
+    return rc;
+  }
+
+  Widget buildFrame() {
+    final padding = GlobalThemeData.padding;
+    scopeController.text = _fieldData.scope;
+    nameController.text = _fieldData.name;
+    valueController.text = _fieldData.value;
+    positionController.text = asString(_fieldData.position);
+    final formItems = <FormItem>[
+      FormItem(
+          TextFormField(
+              controller: scopeController,
+              decoration: InputDecoration(labelText: i18n.tr('Scope')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.scope = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: nameController,
+              decoration: InputDecoration(labelText: i18n.tr('Name')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.name = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: valueController,
+              decoration: InputDecoration(labelText: i18n.tr('Value')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.value = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: positionController,
+              decoration: InputDecoration(labelText: i18n.tr('Position')),
+              onSaved: (value) => _fieldData.position =
+                  jsonToObject(value ?? '', dataType: DataType.int)),
+          weight: 6),
+      FormItem(
+          ElevatedButton(
+              onPressed: () => onVerifyAndStore(),
+              child: Text(i18n.tr('Save'))),
+          weight: 8,
+          gapAbove: 2 * padding),
+      FormItem(
+          ElevatedButton(
+            onPressed: () {
+              attendedPage.pageStates.dbDataState.clear();
+              globalData.navigate(context, '/Scopes/list');
+            },
+            child: Text(i18n.tr('Cancel')),
+          ),
+          weight: 4)
+    ];
+    final rc = Form(
+        key: _formKey,
+        child: Card(
+            margin:
+                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
+            child: Padding(
+                padding: EdgeInsets.symmetric(
+                    vertical: padding, horizontal: padding),
+                child: WidgetForm.flexibleGrid(
+                  formItems,
+                  screenWidth: attendedPage.pageStates.screenWidth,
+                  padding: padding,
+                ))));
+    return rc;
+  }
+
+  @override
+  void dispose() {
+    helperDummyUsage(DataType.int);
+    globalWidgetDummyUsage();
+    scopeController.dispose();
+    nameController.dispose();
+    valueController.dispose();
+    positionController.dispose();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void onStore() {
+    final parameters = <String, dynamic>{
+      'module': 'Scopes',
+      'sql': 'insert',
+    };
+    _fieldData.toMap(parameters);
+    globalData.restPersistence
+        .store(what: 'store', map: parameters)
+        .then((answer) {
+      if (answer.startsWith('id:')) {
+        final id = int.tryParse(answer.substring(3));
+        if (id == null || id == 0) {
+          setError(i18n.tr('Saving data failed: $answer'));
+          setState(() => 1);
+        } else {
+          attendedPage.pageStates.dbDataState.clear();
+          globalData.navigate(context, '/Scopes/edit;$id');
+        }
+      }
+    });
+  }
+
+  void onVerifyAndStore() {
+    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      onStore();
+    }
+  }
+}
+
+class _FieldData {
+  String scope = '';
+  String name = '';
+  String value = '';
+  int position = 0;
+
+  void toMap(Map<String, dynamic> map) {
+    map[':scope'] = scope;
+    map[':name'] = name;
+    map[':value'] = value;
+    map[':position'] = asString(position, dbFormat: true);
+    map[':createdBy'] = GlobalData.loginUserName;
+  }
+}
diff --git a/lib/page/scopes/create_scope_page.dart b/lib/page/scopes/create_scope_page.dart
new file mode 100644 (file)
index 0000000..790dcda
--- /dev/null
@@ -0,0 +1,64 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/scopes_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'create_scope_custom.dart';
+
+class CreateScopePage extends StatefulWidget {
+  final PageStates pageStates = PageStates();
+
+  CreateScopePage() : super();
+
+  @override
+  _CreateScopePageState createState() {
+    final rc = _CreateScopePageState();
+    rc.attendedPage = AttendedPage(
+        this,
+        rc,
+        GlobalData(),
+        ScopesMeta.instance.pageByName('create')!,
+        ScopesMeta.instance,
+        pageStates,
+        (afterReload, rebuild) =>
+            rc.reload(afterReload: afterReload, rebuild: rebuild));
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _CreateScopePageState extends CreateScopeCustom {
+  _CreateScopePageState() : super();
+
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage.pageStates.screenWidth = size.width;
+    attendedPage.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+
+  /// Renders the widget tree again.
+  ///
+  /// [afterReload] is a function used as parameter of setState().
+  ///
+  /// If [rebuild] is true the state has been changed and didChangeDependencies()
+  /// will be called.
+  void reload({Function? afterReload, bool rebuild = false}) {
+    if (afterReload == null) {
+      if (rebuild) {
+        setState(() => didChangeDependencies());
+      } else {
+        setState(() => 1);
+      }
+    } else {
+      setState(() {
+        afterReload();
+        if (rebuild) {
+          didChangeDependencies();
+        }
+      });
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/page/scopes/delete_scope_custom.dart b/lib/page/scopes/delete_scope_custom.dart
new file mode 100644 (file)
index 0000000..f967199
--- /dev/null
@@ -0,0 +1,178 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../base/i18n.dart';
+import '../../base/validators.dart';
+import '../../persistence/persistence.dart';
+import '../../services/global_widget.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/message_line.dart';
+import '../../widget/widget_form.dart';
+import 'delete_scope_page.dart';
+
+final i18n = I18N();
+
+class DeleteScopeCustom extends State<DeleteScopePage> with MessageLine {
+  final int primaryKey;
+  final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
+  late AttendedPage attendedPage;
+  final _fieldData = _FieldData();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'DeleteScope');
+  final scopeController = TextEditingController();
+  final nameController = TextEditingController();
+  final valueController = TextEditingController();
+  final positionController = TextEditingController();
+
+  DeleteScopeCustom(this.primaryKey);
+
+  @override
+  Widget build(BuildContext context) {
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Delete Scope')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: FutureBuilder<DbData>(
+                future: _futureDbData,
+                builder: (context, snapshot) {
+                  final rc = attendedPage.loadRecord(snapshot, (record) {
+                    _fieldData.fromMap(record);
+                    return buildFrame();
+                  });
+                  return rc;
+                })));
+    return rc;
+  }
+
+  Widget buildFrame() {
+    final padding = GlobalThemeData.padding;
+    scopeController.text = _fieldData.scope;
+    nameController.text = _fieldData.name;
+    valueController.text = _fieldData.value;
+    positionController.text = asString(_fieldData.position);
+    final formItems = <FormItem>[
+      FormItem(
+          TextFormField(
+              controller: scopeController,
+              decoration: InputDecoration(labelText: i18n.tr('Scope')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.scope = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: nameController,
+              decoration: InputDecoration(labelText: i18n.tr('Name')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.name = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: valueController,
+              decoration: InputDecoration(labelText: i18n.tr('Value')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.value = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: positionController,
+              decoration: InputDecoration(labelText: i18n.tr('Position')),
+              onSaved: (value) => _fieldData.position =
+                  jsonToObject(value ?? '', dataType: DataType.int)),
+          weight: 6),
+      FormItem(
+          ElevatedButton(
+              onPressed: () => onVerifyAndDelete(),
+              child: Text(i18n.tr('Delete'))),
+          weight: 8,
+          gapAbove: 2 * padding),
+      FormItem(
+          ElevatedButton(
+            onPressed: () {
+              attendedPage.pageStates.dbDataState.clear();
+              globalData.navigate(context, '/Scopes/list');
+            },
+            child: Text(i18n.tr('Cancel')),
+          ),
+          weight: 4)
+    ];
+    final rc = Form(
+        key: _formKey,
+        child: Card(
+            margin:
+                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
+            child: Padding(
+                padding: EdgeInsets.symmetric(
+                    vertical: padding, horizontal: padding),
+                child: WidgetForm.flexibleGrid(
+                  formItems,
+                  screenWidth: attendedPage.pageStates.screenWidth,
+                  padding: padding,
+                ))));
+    return rc;
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    requestRecord();
+  }
+
+  @override
+  void dispose() {
+    helperDummyUsage(DataType.int);
+    globalWidgetDummyUsage();
+    scopeController.dispose();
+    nameController.dispose();
+    valueController.dispose();
+    positionController.dispose();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void requestRecord() => _futureDbData = globalData.restPersistence.query(
+      what: 'query',
+      data: {'module': 'Scopes', 'sql': 'byId', ':id': primaryKey});
+
+  void onDelete() {
+    final parameters = <String, dynamic>{
+      'module': 'Scopes',
+      'sql': 'delete',
+      ':id': primaryKey
+    };
+    globalData.restPersistence
+        .store(what: 'store', map: parameters)
+        .then((answer) {
+      globalData.navigate(context, '/Scopes/list');
+    });
+  }
+
+  void onVerifyAndDelete() {
+    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      onDelete();
+    }
+  }
+}
+
+class _FieldData {
+  String scope = '';
+  String name = '';
+  String value = '';
+  int position = 0;
+
+  void fromMap(Map<String, dynamic> map) {
+    scope = map['scope_scope'];
+    name = map['scope_name'];
+    value = map['scope_value'];
+    position = jsonToObject(map['scope_position'], dataType: DataType.int);
+  }
+}
diff --git a/lib/page/scopes/delete_scope_page.dart b/lib/page/scopes/delete_scope_page.dart
new file mode 100644 (file)
index 0000000..d49cad1
--- /dev/null
@@ -0,0 +1,65 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/scopes_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'delete_scope_custom.dart';
+
+class DeleteScopePage extends StatefulWidget {
+  final int primaryKey;
+  final PageStates pageStates = PageStates();
+
+  DeleteScopePage(this.primaryKey) : super();
+
+  @override
+  _DeleteScopePageState createState() {
+    final rc = _DeleteScopePageState(this.primaryKey);
+    rc.attendedPage = AttendedPage(
+        this,
+        rc,
+        GlobalData(),
+        ScopesMeta.instance.pageByName('delete')!,
+        ScopesMeta.instance,
+        pageStates,
+        (afterReload, rebuild) =>
+            rc.reload(afterReload: afterReload, rebuild: rebuild));
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _DeleteScopePageState extends DeleteScopeCustom {
+  _DeleteScopePageState(int primaryKey) : super(primaryKey);
+
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage.pageStates.screenWidth = size.width;
+    attendedPage.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+
+  /// Renders the widget tree again.
+  ///
+  /// [afterReload] is a function used as parameter of setState().
+  ///
+  /// If [rebuild] is true the state has been changed and didChangeDependencies()
+  /// will be called.
+  void reload({Function? afterReload, bool rebuild = false}) {
+    if (afterReload == null) {
+      if (rebuild) {
+        setState(() => didChangeDependencies());
+      } else {
+        setState(() => 1);
+      }
+    } else {
+      setState(() {
+        afterReload();
+        if (rebuild) {
+          didChangeDependencies();
+        }
+      });
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/page/scopes/edit_scope_custom.dart b/lib/page/scopes/edit_scope_custom.dart
new file mode 100644 (file)
index 0000000..9eef671
--- /dev/null
@@ -0,0 +1,190 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../base/i18n.dart';
+import '../../base/validators.dart';
+import '../../persistence/persistence.dart';
+import '../../services/global_widget.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/message_line.dart';
+import '../../widget/widget_form.dart';
+import 'edit_scope_page.dart';
+
+final i18n = I18N();
+
+class EditScopeCustom extends State<EditScopePage> with MessageLine {
+  final int primaryKey;
+  final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
+  late AttendedPage attendedPage;
+  final _fieldData = _FieldData();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'EditScope');
+  final scopeController = TextEditingController();
+  final nameController = TextEditingController();
+  final valueController = TextEditingController();
+  final positionController = TextEditingController();
+
+  EditScopeCustom(this.primaryKey);
+
+  @override
+  Widget build(BuildContext context) {
+    final rc = Scaffold(
+        appBar: globalData.appBarBuilder(i18n.tr('Change Scope')),
+        drawer: globalData.drawerBuilder(context),
+        body: SafeArea(
+            child: FutureBuilder<DbData>(
+                future: _futureDbData,
+                builder: (context, snapshot) {
+                  final rc = attendedPage.loadRecord(snapshot, (record) {
+                    _fieldData.fromMap(record);
+                    return buildFrame();
+                  });
+                  return rc;
+                })));
+    return rc;
+  }
+
+  Widget buildFrame() {
+    final padding = GlobalThemeData.padding;
+    scopeController.text = _fieldData.scope;
+    nameController.text = _fieldData.name;
+    valueController.text = _fieldData.value;
+    positionController.text = asString(_fieldData.position);
+    final formItems = <FormItem>[
+      FormItem(
+          TextFormField(
+              controller: scopeController,
+              decoration: InputDecoration(labelText: i18n.tr('Scope')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.scope = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: nameController,
+              decoration: InputDecoration(labelText: i18n.tr('Name')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.name = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: valueController,
+              decoration: InputDecoration(labelText: i18n.tr('Value')),
+              validator: (input) => notEmpty(input),
+              onSaved: (value) => _fieldData.value = value ?? ''),
+          weight: 6),
+      FormItem(
+          TextFormField(
+              controller: positionController,
+              decoration: InputDecoration(labelText: i18n.tr('Position')),
+              onSaved: (value) => _fieldData.position =
+                  jsonToObject(value ?? '', dataType: DataType.int)),
+          weight: 6),
+      FormItem(
+          ElevatedButton(
+              onPressed: () => onVerifyAndStore(),
+              child: Text(i18n.tr('Save'))),
+          weight: 8,
+          gapAbove: 2 * padding),
+      FormItem(
+          ElevatedButton(
+            onPressed: () {
+              attendedPage.pageStates.dbDataState.clear();
+              globalData.navigate(context, '/Scopes/list');
+            },
+            child: Text(i18n.tr('Cancel')),
+          ),
+          weight: 4)
+    ];
+    final rc = Form(
+        key: _formKey,
+        child: Card(
+            margin:
+                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
+            child: Padding(
+                padding: EdgeInsets.symmetric(
+                    vertical: padding, horizontal: padding),
+                child: WidgetForm.flexibleGrid(
+                  formItems,
+                  screenWidth: attendedPage.pageStates.screenWidth,
+                  padding: padding,
+                ))));
+    return rc;
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    requestRecord();
+  }
+
+  @override
+  void dispose() {
+    helperDummyUsage(DataType.int);
+    globalWidgetDummyUsage();
+    scopeController.dispose();
+    nameController.dispose();
+    valueController.dispose();
+    positionController.dispose();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void requestRecord() => _futureDbData = globalData.restPersistence.query(
+      what: 'query',
+      data: {'module': 'Scopes', 'sql': 'byId', ':id': primaryKey});
+
+  void onStore() {
+    final parameters = <String, dynamic>{
+      'module': 'Scopes',
+      'sql': 'update',
+      ':id': primaryKey
+    };
+    _fieldData.toMap(parameters);
+    globalData.restPersistence
+        .store(what: 'store', map: parameters)
+        .then((answer) {
+      requestRecord();
+      attendedPage.pageStates.dbDataState.clear();
+      setState(() => 1);
+    });
+  }
+
+  void onVerifyAndStore() {
+    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      onStore();
+    }
+  }
+}
+
+class _FieldData {
+  String scope = '';
+  String name = '';
+  String value = '';
+  int position = 0;
+
+  void fromMap(Map<String, dynamic> map) {
+    scope = map['scope_scope'];
+    name = map['scope_name'];
+    value = map['scope_value'];
+    position = jsonToObject(map['scope_position'], dataType: DataType.int);
+  }
+
+  void toMap(Map<String, dynamic> map) {
+    // please set outside: map[':id'] = primaryKey;
+    map[':scope'] = scope;
+    map[':name'] = name;
+    map[':value'] = value;
+    map[':position'] = asString(position, dbFormat: true);
+    map[':changedBy'] = GlobalData.loginUserName;
+  }
+}
diff --git a/lib/page/scopes/edit_scope_page.dart b/lib/page/scopes/edit_scope_page.dart
new file mode 100644 (file)
index 0000000..a6c80d5
--- /dev/null
@@ -0,0 +1,65 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/scopes_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'edit_scope_custom.dart';
+
+class EditScopePage extends StatefulWidget {
+  final int primaryKey;
+  final PageStates pageStates = PageStates();
+
+  EditScopePage(this.primaryKey) : super();
+
+  @override
+  _EditScopePageState createState() {
+    final rc = _EditScopePageState(this.primaryKey);
+    rc.attendedPage = AttendedPage(
+        this,
+        rc,
+        GlobalData(),
+        ScopesMeta.instance.pageByName('edit')!,
+        ScopesMeta.instance,
+        pageStates,
+        (afterReload, rebuild) =>
+            rc.reload(afterReload: afterReload, rebuild: rebuild));
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _EditScopePageState extends EditScopeCustom {
+  _EditScopePageState(int primaryKey) : super(primaryKey);
+
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage.pageStates.screenWidth = size.width;
+    attendedPage.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+
+  /// Renders the widget tree again.
+  ///
+  /// [afterReload] is a function used as parameter of setState().
+  ///
+  /// If [rebuild] is true the state has been changed and didChangeDependencies()
+  /// will be called.
+  void reload({Function? afterReload, bool rebuild = false}) {
+    if (afterReload == null) {
+      if (rebuild) {
+        setState(() => didChangeDependencies());
+      } else {
+        setState(() => 1);
+      }
+    } else {
+      setState(() {
+        afterReload();
+        if (rebuild) {
+          didChangeDependencies();
+        }
+      });
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/page/scopes/list_scope_custom.dart b/lib/page/scopes/list_scope_custom.dart
new file mode 100644 (file)
index 0000000..a3ba05a
--- /dev/null
@@ -0,0 +1,178 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../base/i18n.dart';
+import '../../services/global_widget.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import '../../persistence/persistence.dart';
+import 'list_scope_page.dart';
+
+final i18n = I18N();
+
+class ListScopeCustom extends State<ListScopePage> {
+  final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
+  late AttendedPage attendedPage;
+  final _fieldData = _FieldData();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'CreateScope');
+  final textController = TextEditingController();
+
+  ListScopeCustom();
+
+  @override
+  Widget build(BuildContext context) {
+    final rc = Scaffold(
+      appBar: globalData.appBarBuilder(i18n.tr('Overview scopes')),
+      drawer: globalData.drawerBuilder(context),
+      floatingActionButton: FloatingActionButton(
+          onPressed: () {
+            globalData.navigate(context, '/Scopes/create');
+          },
+          child: const Icon(Icons.add),
+          tooltip: 'Add a scope item'),
+      body: SafeArea(
+          child: FutureBuilder<DbData>(
+        future: _futureDbData,
+        builder: (context, snapshot) {
+          Widget rc;
+          if (snapshot.connectionState != ConnectionState.done) {
+            rc = const CircularProgressIndicator();
+          } else {
+            if (snapshot.hasData) {
+              final rows = attendedPage.getRows(
+                  dbData: snapshot.data!,
+                  columnList:
+                      'scope_id;scope_scope;scope_name;scope_value;scope_position',
+                  onDone: () => setState(() => 1),
+                  routeEdit: '/Scopes/edit',
+                  context: context);
+              rc = buildFrame(
+                  totalCount: snapshot.data?.count ?? rows.length, rows: rows);
+            } else if (snapshot.hasError) {
+              rc = Text('Backend problem: ${snapshot.error}');
+            } else {
+              rc = const CircularProgressIndicator();
+            }
+          }
+          return rc;
+        },
+      )),
+    );
+    return rc;
+  }
+
+  Widget buildFrame({required JsonList rows, required int totalCount}) {
+    final padding = GlobalThemeData.padding;
+    final formItems = <FormItem>[
+      FormItem(
+          TextFormField(
+              controller: textController,
+              decoration: InputDecoration(labelText: i18n.tr('Text')),
+              onSaved: (value) => _fieldData.text = value ?? ''),
+          weight: 6),
+      FormItem(
+          ElevatedButton(
+              onPressed: () => search(), child: Text(i18n.tr('Search'))),
+          weight: 12,
+          gapAbove: padding),
+    ];
+    final form = Form(
+        key: _formKey,
+        child: Card(
+            color: GlobalThemeData.formBackgroundColor,
+            elevation: GlobalThemeData.formElevation,
+            margin:
+                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
+            child: Padding(
+                padding: EdgeInsets.symmetric(
+                    vertical: padding, horizontal: padding),
+                child: WidgetForm.flexibleGrid(formItems,
+                    screenWidth: attendedPage.pageStates.screenWidth,
+                    padding: padding))));
+    final table = DataTable(
+      columns: <DataColumn>[
+        DataColumn(
+          label: Text(i18n.tr('Id')),
+        ),
+        DataColumn(
+          label: Text(i18n.tr('Scope')),
+        ),
+        DataColumn(
+          label: Text(i18n.tr('Name')),
+        ),
+        DataColumn(
+          label: Text(i18n.tr('Value')),
+        ),
+        DataColumn(
+          label: Text(i18n.tr('Position')),
+        ),
+      ],
+      rows: rows as List<DataRow>,
+    );
+    Widget? tabBar = attendedPage.buildChipBar(
+        totalCount: totalCount,
+        offset: _fieldData.theOffset,
+        pageSize: _fieldData.thePageSize,
+        onTap: (offset) {
+          _fieldData.theOffset = offset;
+          requestRecords();
+          setState(() => 1);
+        });
+    final frameWidget = ListView(children: [
+      form,
+      if (tabBar != null) tabBar,
+      SizedBox(height: padding),
+      SizedBox(width: double.infinity, child: table),
+    ]);
+    return frameWidget;
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    requestRecords();
+  }
+
+  @override
+  void dispose() {
+    helperDummyUsage(DataType.string);
+    globalWidgetDummyUsage();
+    textController.dispose();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void requestRecords() =>
+      _futureDbData = globalData.restPersistence.query(what: 'query', data: {
+        'module': 'Scopes',
+        'sql': 'list',
+        'offset': _fieldData.theOffset,
+        'size': _fieldData.thePageSize,
+        ':text': _fieldData.text,
+      });
+
+  void search() {
+    attendedPage.pageStates.dbDataState.clear();
+    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      requestRecords();
+      setState(() => 1);
+    }
+  }
+}
+
+class _FieldData {
+  int thePageSize = 10;
+  int theOffset = 0;
+  String text = '';
+}
diff --git a/lib/page/scopes/list_scope_page.dart b/lib/page/scopes/list_scope_page.dart
new file mode 100644 (file)
index 0000000..0c5c506
--- /dev/null
@@ -0,0 +1,64 @@
+// DO NOT CHANGE. This file is created by the meta_tool!
+import 'package:flutter/material.dart';
+
+import '../../meta/scopes_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import 'list_scope_custom.dart';
+
+class ListScopePage extends StatefulWidget {
+  final PageStates pageStates = PageStates();
+
+  ListScopePage() : super();
+
+  @override
+  _ListScopePageState createState() {
+    final rc = _ListScopePageState();
+    rc.attendedPage = AttendedPage(
+        this,
+        rc,
+        GlobalData(),
+        ScopesMeta.instance.pageByName('list')!,
+        ScopesMeta.instance,
+        pageStates,
+        (afterReload, rebuild) =>
+            rc.reload(afterReload: afterReload, rebuild: rebuild));
+    pageStates.attendedPage = rc.attendedPage;
+    return rc;
+  }
+}
+
+class _ListScopePageState extends ListScopeCustom {
+  _ListScopePageState() : super();
+
+  @override
+  void didChangeDependencies() {
+    final size = MediaQuery.of(context).size;
+    attendedPage.pageStates.screenWidth = size.width;
+    attendedPage.pageStates.screenHeight = size.height;
+    super.didChangeDependencies();
+  }
+
+  /// Renders the widget tree again.
+  ///
+  /// [afterReload] is a function used as parameter of setState().
+  ///
+  /// If [rebuild] is true the state has been changed and didChangeDependencies()
+  /// will be called.
+  void reload({Function? afterReload, bool rebuild = false}) {
+    if (afterReload == null) {
+      if (rebuild) {
+        setState(() => didChangeDependencies());
+      } else {
+        setState(() => 1);
+      }
+    } else {
+      setState(() {
+        afterReload();
+        if (rebuild) {
+          didChangeDependencies();
+        }
+      });
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/page/scopes/scope_data.dart b/lib/page/scopes/scope_data.dart
new file mode 100644 (file)
index 0000000..64fa61b
--- /dev/null
@@ -0,0 +1,126 @@
+// DO NOT CHANGE. This file is created by the meta_tool
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../persistence/data_record.dart';
+
+class ScopeData extends DataRecord<int> {
+  int? id;
+  String? scope;
+  String? name;
+  String? value;
+  int? position;
+  DateTime? createdAt;
+  String? createdBy;
+  DateTime? changedAt;
+  String? changedBy;
+
+  ScopeData(
+      {this.id,
+      this.scope,
+      this.name,
+      this.value,
+      this.position,
+      this.createdAt,
+      this.createdBy,
+      this.changedAt,
+      this.changedBy});
+
+  ScopeData.createFromMap(DataMap map) {
+    fromMap(map);
+  }
+
+  @override
+  void fromMap(DataMap map) {
+    id = map.containsKey('scope_id')
+        ? fromString(map['scope_id'], dataType: DataType.reference)
+        : null;
+    scope = map.containsKey('scope_scope')
+        ? fromString(map['scope_scope'], dataType: DataType.string)
+        : null;
+    name = map.containsKey('scope_name')
+        ? fromString(map['scope_name'], dataType: DataType.string)
+        : null;
+    value = map.containsKey('scope_value')
+        ? fromString(map['scope_value'], dataType: DataType.string)
+        : null;
+    position = map.containsKey('scope_position')
+        ? fromString(map['scope_position'], dataType: DataType.int)
+        : null;
+    createdAt = map.containsKey('scope_createdat')
+        ? fromString(map['scope_createdat'], dataType: DataType.datetime)
+        : null;
+    createdBy = map.containsKey('scope_createdby')
+        ? fromString(map['scope_createdby'], dataType: DataType.string)
+        : null;
+    changedAt = map.containsKey('scope_changedat')
+        ? fromString(map['scope_changedat'], dataType: DataType.datetime)
+        : null;
+    changedBy = map.containsKey('scope_changedby')
+        ? fromString(map['scope_changedby'], dataType: DataType.string)
+        : null;
+  }
+
+  @override
+  int keyOf() {
+    return id ?? 0;
+  }
+
+  @override
+  String nameOfKey() {
+    return 'scope_id';
+  }
+
+  static DataType? dataTypeOf(String name) {
+    DataType? rc;
+    switch (name) {
+      case 'id':
+        rc = DataType.reference;
+        break;
+      case 'scope':
+        rc = DataType.string;
+        break;
+      case 'name':
+        rc = DataType.string;
+        break;
+      case 'value':
+        rc = DataType.string;
+        break;
+      case 'position':
+        rc = DataType.int;
+        break;
+      case 'createdAt':
+        rc = DataType.datetime;
+        break;
+      case 'createdBy':
+        rc = DataType.string;
+        break;
+      case 'changedAt':
+        rc = DataType.datetime;
+        break;
+      case 'changedBy':
+        rc = DataType.string;
+        break;
+      default:
+        break;
+    }
+    return rc;
+  }
+
+  @override
+  DataMap toMap({DataMap? map, bool clear = true}) {
+    map ??= DataMap();
+    if (clear) {
+      map.clear();
+    }
+    map['scope_id'] = id;
+    map['scope_scope'] = scope;
+    map['scope_name'] = name;
+    map['scope_value'] = value;
+    map['scope_position'] = position;
+    map['scope_createdat'] = createdAt;
+    map['scope_createdby'] = createdBy;
+    map['scope_changedat'] = changedAt;
+    map['scope_changedby'] = changedBy;
+    return map;
+  }
+}
index 58346477b65c9d87d5e1e5c4f02c3d9fa46869e9..f32e54e426fab003234fa53c878a1f8d9b7190d4 100644 (file)
@@ -28,6 +28,7 @@ class CreateStarterPage extends StatefulWidget {
 
 class _CreateStarterPageState extends CreateStarterCustom {
   _CreateStarterPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _CreateStarterPageState extends CreateStarterCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 0d16fe3ea31e72e54bd383d6dc0dbdae12732829..9a944edb55c703a76e9fc3a7ce6c935f3714d0e6 100644 (file)
@@ -29,6 +29,7 @@ class DeleteStarterPage extends StatefulWidget {
 
 class _DeleteStarterPageState extends DeleteStarterCustom {
   _DeleteStarterPageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _DeleteStarterPageState extends DeleteStarterCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 2768e2587174fc2372cde82ac00213b634ed5212..553d8dded4551abf5e93f28eea3343915159c0cf 100644 (file)
@@ -29,6 +29,7 @@ class EditStarterPage extends StatefulWidget {
 
 class _EditStarterPageState extends EditStarterCustom {
   _EditStarterPageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _EditStarterPageState extends EditStarterCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 795a7d2e8e65a758a13dbae8c00e5acc38f82067..974a2e3e87178e066b444e53ee487f7beb1c0094 100644 (file)
@@ -28,6 +28,7 @@ class ListStarterPage extends StatefulWidget {
 
 class _ListStarterPageState extends ListStarterCustom {
   _ListStarterPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _ListStarterPageState extends ListStarterCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 46c80855e7912746981fa823e4f1c32dd759220b..37bffc1e319468da98f86c0ce171570ff8dd21ea 100644 (file)
@@ -2,7 +2,6 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-
 class StarterData extends DataRecord<int> {
   int? id;
   String? name;
@@ -12,6 +11,7 @@ class StarterData extends DataRecord<int> {
   String? createdBy;
   DateTime? changedAt;
   String? changedBy;
+
   StarterData(
       {this.id,
       this.name,
@@ -21,9 +21,11 @@ class StarterData extends DataRecord<int> {
       this.createdBy,
       this.changedAt,
       this.changedBy});
+
   StarterData.createFromMap(DataMap map) {
     fromMap(map);
   }
+
   @override
   void fromMap(DataMap map) {
     id = map.containsKey('starter_id')
@@ -94,7 +96,6 @@ class StarterData extends DataRecord<int> {
     }
     return rc;
   }
-
   @override
   DataMap toMap({DataMap? map, bool clear = true}) {
     map ??= DataMap();
diff --git a/lib/page/structures/create_structure_custom.dart b/lib/page/structures/create_structure_custom.dart
deleted file mode 100644 (file)
index 13de279..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-// This file is created by the meta_tool. But it can be customized.
-// It will never overridden by the meta_tool.
-import 'package:flutter/material.dart';
-
-import '../../base/defines.dart';
-import '../../base/helper.dart';
-import '../../base/i18n.dart';
-import '../../base/validators.dart';
-import '../../services/global_widget.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import '../../widget/message_line.dart';
-import '../../widget/widget_form.dart';
-import 'create_structure_page.dart';
-
-final i18n = I18N();
-
-class CreateStructureCustom extends State<CreateStructurePage>
-    with MessageLine {
-  final globalData = GlobalData();
-  late AttendedPage attendedPage;
-  final _fieldData = _FieldData();
-  final GlobalKey<FormState> _formKey =
-      GlobalKey<FormState>(debugLabel: 'CreateStructure');
-  final scopeController = TextEditingController();
-  final nameController = TextEditingController();
-  final valueController = TextEditingController();
-  final positionController = TextEditingController();
-  CreateStructureCustom();
-  @override
-  Widget build(BuildContext context) {
-    final rc = Scaffold(
-        appBar: globalData.appBarBuilder(i18n.tr('New Structure')),
-        drawer: globalData.drawerBuilder(context),
-        body: SafeArea(child: buildFrame()));
-    return rc;
-  }
-
-  Widget buildFrame() {
-    final padding = GlobalThemeData.padding;
-    scopeController.text = _fieldData.scope;
-    nameController.text = _fieldData.name;
-    valueController.text = _fieldData.value;
-    positionController.text = asString(_fieldData.position);
-    final formItems = <FormItem>[
-      FormItem(
-          TextFormField(
-              controller: scopeController,
-              decoration: InputDecoration(labelText: i18n.tr('Scope')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.scope = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: nameController,
-              decoration: InputDecoration(labelText: i18n.tr('Name')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.name = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: valueController,
-              decoration: InputDecoration(labelText: i18n.tr('Value')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.value = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: positionController,
-              decoration: InputDecoration(labelText: i18n.tr('Position')),
-              onSaved: (value) => _fieldData.position =
-                  jsonToObject(value ?? '', dataType: DataType.int)),
-          weight: 6),
-      FormItem(
-          ElevatedButton(
-              onPressed: () => onVerifyAndStore(),
-              child: Text(i18n.tr('Save'))),
-          weight: 8,
-          gapAbove: 2 * padding),
-      FormItem(
-          ElevatedButton(
-            onPressed: () {
-              attendedPage.pageStates.dbDataState.clear();
-              globalData.navigate(context, '/Structures/list');
-            },
-            child: Text(i18n.tr('Cancel')),
-          ),
-          weight: 4)
-    ];
-    final rc = Form(
-        key: _formKey,
-        child: Card(
-            margin:
-                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
-            child: Padding(
-                padding: EdgeInsets.symmetric(
-                    vertical: padding, horizontal: padding),
-                child: WidgetForm.flexibleGrid(
-                  formItems,
-                  screenWidth: attendedPage.pageStates.screenWidth,
-                  padding: padding,
-                ))));
-    return rc;
-  }
-
-  @override
-  void dispose() {
-    helperDummyUsage(DataType.int);
-    globalWidgetDummyUsage();
-    scopeController.dispose();
-    nameController.dispose();
-    valueController.dispose();
-    positionController.dispose();
-    super.dispose();
-  }
-
-  @override
-  void initState() {
-    super.initState();
-  }
-
-  void onStore() {
-    final parameters = <String, dynamic>{
-      'module': 'Structures',
-      'sql': 'insert',
-    };
-    _fieldData.toMap(parameters);
-    globalData.restPersistence
-        .store(what: 'store', map: parameters)
-        .then((answer) {
-      if (answer.startsWith('id:')) {
-        final id = int.tryParse(answer.substring(3));
-        if (id == null || id == 0) {
-          setError(i18n.tr('Saving data failed: $answer'));
-          setState(() => 1);
-        } else {
-          attendedPage.pageStates.dbDataState.clear();
-          globalData.navigate(context, '/Structures/edit;$id');
-        }
-      }
-    });
-  }
-
-  void onVerifyAndStore() {
-    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
-      _formKey.currentState!.save();
-      onStore();
-    }
-  }
-}
-
-class _FieldData {
-  String scope = '';
-  String name = '';
-  String value = '';
-  int position = 0;
-
-  void toMap(Map<String, dynamic> map) {
-    map[':scope'] = scope;
-    map[':name'] = name;
-    map[':value'] = value;
-    map[':position'] = asString(position, dbFormat: true);
-    map[':createdBy'] = GlobalData.loginUserName;
-  }
-}
diff --git a/lib/page/structures/create_structure_page.dart b/lib/page/structures/create_structure_page.dart
deleted file mode 100644 (file)
index ab007e1..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-// DO NOT CHANGE. This file is created by the meta_tool!
-import 'package:flutter/material.dart';
-
-import '../../meta/structures_meta.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import 'create_structure_custom.dart';
-
-class CreateStructurePage extends StatefulWidget {
-  final PageStates pageStates = PageStates();
-  CreateStructurePage() : super();
-  @override
-  _CreateStructurePageState createState() {
-    final rc = _CreateStructurePageState();
-    rc.attendedPage = AttendedPage(
-        this,
-        rc,
-        GlobalData(),
-        StructuresMeta.instance.pageByName('create')!,
-        StructuresMeta.instance,
-        pageStates,
-        (afterReload, rebuild) =>
-            rc.reload(afterReload: afterReload, rebuild: rebuild));
-    pageStates.attendedPage = rc.attendedPage;
-    return rc;
-  }
-}
-
-class _CreateStructurePageState extends CreateStructureCustom {
-  _CreateStructurePageState() : super();
-  @override
-  void didChangeDependencies() {
-    final size = MediaQuery.of(context).size;
-    attendedPage.pageStates.screenWidth = size.width;
-    attendedPage.pageStates.screenHeight = size.height;
-    super.didChangeDependencies();
-  }
-
-  /// Renders the widget tree again.
-  ///
-  /// [afterReload] is a function used as parameter of setState().
-  ///
-  /// If [rebuild] is true the state has been changed and didChangeDependencies()
-  /// will be called.
-  void reload({Function? afterReload, bool rebuild = false}) {
-    if (afterReload == null) {
-      if (rebuild) {
-        setState(() => didChangeDependencies());
-      } else {
-        setState(() => 1);
-      }
-    } else {
-      setState(() {
-        afterReload();
-        if (rebuild) {
-          didChangeDependencies();
-        }
-      });
-    }
-  }
-}
diff --git a/lib/page/structures/delete_structure_custom.dart b/lib/page/structures/delete_structure_custom.dart
deleted file mode 100644 (file)
index 043bc26..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-// This file is created by the meta_tool. But it can be customized.
-// It will never overridden by the meta_tool.
-import 'package:flutter/material.dart';
-
-import '../../base/defines.dart';
-import '../../base/helper.dart';
-import '../../base/i18n.dart';
-import '../../base/validators.dart';
-import '../../persistence/persistence.dart';
-import '../../services/global_widget.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import '../../widget/message_line.dart';
-import '../../widget/widget_form.dart';
-import 'delete_structure_page.dart';
-
-final i18n = I18N();
-
-class DeleteStructureCustom extends State<DeleteStructurePage>
-    with MessageLine {
-  final int primaryKey;
-  final globalData = GlobalData();
-  late Future<DbData> _futureDbData;
-  late AttendedPage attendedPage;
-  final _fieldData = _FieldData();
-  final GlobalKey<FormState> _formKey =
-      GlobalKey<FormState>(debugLabel: 'DeleteStructure');
-  final scopeController = TextEditingController();
-  final nameController = TextEditingController();
-  final valueController = TextEditingController();
-  final positionController = TextEditingController();
-  DeleteStructureCustom(this.primaryKey);
-  @override
-  Widget build(BuildContext context) {
-    final rc = Scaffold(
-        appBar: globalData.appBarBuilder(i18n.tr('Delete Structure')),
-        drawer: globalData.drawerBuilder(context),
-        body: SafeArea(
-            child: FutureBuilder<DbData>(
-                future: _futureDbData,
-                builder: (context, snapshot) {
-                  final rc = attendedPage.loadRecord(snapshot, (record) {
-                    _fieldData.fromMap(record);
-                    return buildFrame();
-                  });
-                  return rc;
-                })));
-    return rc;
-  }
-
-  Widget buildFrame() {
-    final padding = GlobalThemeData.padding;
-    scopeController.text = _fieldData.scope;
-    nameController.text = _fieldData.name;
-    valueController.text = _fieldData.value;
-    positionController.text = asString(_fieldData.position);
-    final formItems = <FormItem>[
-      FormItem(
-          TextFormField(
-              controller: scopeController,
-              decoration: InputDecoration(labelText: i18n.tr('Scope')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.scope = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: nameController,
-              decoration: InputDecoration(labelText: i18n.tr('Name')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.name = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: valueController,
-              decoration: InputDecoration(labelText: i18n.tr('Value')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.value = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: positionController,
-              decoration: InputDecoration(labelText: i18n.tr('Position')),
-              onSaved: (value) => _fieldData.position =
-                  jsonToObject(value ?? '', dataType: DataType.int)),
-          weight: 6),
-      FormItem(
-          ElevatedButton(
-              onPressed: () => onVerifyAndDelete(),
-              child: Text(i18n.tr('Delete'))),
-          weight: 8,
-          gapAbove: 2 * padding),
-      FormItem(
-          ElevatedButton(
-            onPressed: () {
-              attendedPage.pageStates.dbDataState.clear();
-              globalData.navigate(context, '/Structures/list');
-            },
-            child: Text(i18n.tr('Cancel')),
-          ),
-          weight: 4)
-    ];
-    final rc = Form(
-        key: _formKey,
-        child: Card(
-            margin:
-                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
-            child: Padding(
-                padding: EdgeInsets.symmetric(
-                    vertical: padding, horizontal: padding),
-                child: WidgetForm.flexibleGrid(
-                  formItems,
-                  screenWidth: attendedPage.pageStates.screenWidth,
-                  padding: padding,
-                ))));
-    return rc;
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    requestRecord();
-  }
-
-  @override
-  void dispose() {
-    helperDummyUsage(DataType.int);
-    globalWidgetDummyUsage();
-    scopeController.dispose();
-    nameController.dispose();
-    valueController.dispose();
-    positionController.dispose();
-    super.dispose();
-  }
-
-  @override
-  void initState() {
-    super.initState();
-  }
-
-  void requestRecord() => _futureDbData = globalData.restPersistence.query(
-      what: 'query',
-      data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey});
-
-  void onDelete() {
-    final parameters = <String, dynamic>{
-      'module': 'Structures',
-      'sql': 'delete',
-      ':id': primaryKey
-    };
-    globalData.restPersistence
-        .store(what: 'store', map: parameters)
-        .then((answer) {
-      globalData.navigate(context, '/Structures/list');
-    });
-  }
-
-  void onVerifyAndDelete() {
-    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
-      _formKey.currentState!.save();
-      onDelete();
-    }
-  }
-}
-
-class _FieldData {
-  String scope = '';
-  String name = '';
-  String value = '';
-  int position = 0;
-
-  void fromMap(Map<String, dynamic> map) {
-    scope = map['structure_scope'];
-    name = map['structure_name'];
-    value = map['structure_value'];
-    position = jsonToObject(map['structure_position'], dataType: DataType.int);
-  }
-}
diff --git a/lib/page/structures/delete_structure_page.dart b/lib/page/structures/delete_structure_page.dart
deleted file mode 100644 (file)
index dda5535..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// DO NOT CHANGE. This file is created by the meta_tool!
-import 'package:flutter/material.dart';
-
-import '../../meta/structures_meta.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import 'delete_structure_custom.dart';
-
-class DeleteStructurePage extends StatefulWidget {
-  final int primaryKey;
-  final PageStates pageStates = PageStates();
-  DeleteStructurePage(this.primaryKey) : super();
-  @override
-  _DeleteStructurePageState createState() {
-    final rc = _DeleteStructurePageState(this.primaryKey);
-    rc.attendedPage = AttendedPage(
-        this,
-        rc,
-        GlobalData(),
-        StructuresMeta.instance.pageByName('delete')!,
-        StructuresMeta.instance,
-        pageStates,
-        (afterReload, rebuild) =>
-            rc.reload(afterReload: afterReload, rebuild: rebuild));
-    pageStates.attendedPage = rc.attendedPage;
-    return rc;
-  }
-}
-
-class _DeleteStructurePageState extends DeleteStructureCustom {
-  _DeleteStructurePageState(int primaryKey) : super(primaryKey);
-  @override
-  void didChangeDependencies() {
-    final size = MediaQuery.of(context).size;
-    attendedPage.pageStates.screenWidth = size.width;
-    attendedPage.pageStates.screenHeight = size.height;
-    super.didChangeDependencies();
-  }
-
-  /// Renders the widget tree again.
-  ///
-  /// [afterReload] is a function used as parameter of setState().
-  ///
-  /// If [rebuild] is true the state has been changed and didChangeDependencies()
-  /// will be called.
-  void reload({Function? afterReload, bool rebuild = false}) {
-    if (afterReload == null) {
-      if (rebuild) {
-        setState(() => didChangeDependencies());
-      } else {
-        setState(() => 1);
-      }
-    } else {
-      setState(() {
-        afterReload();
-        if (rebuild) {
-          didChangeDependencies();
-        }
-      });
-    }
-  }
-}
diff --git a/lib/page/structures/edit_structure_custom.dart b/lib/page/structures/edit_structure_custom.dart
deleted file mode 100644 (file)
index 309734d..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-// This file is created by the meta_tool. But it can be customized.
-// It will never overridden by the meta_tool.
-import 'package:flutter/material.dart';
-
-import '../../base/defines.dart';
-import '../../base/helper.dart';
-import '../../base/i18n.dart';
-import '../../base/validators.dart';
-import '../../persistence/persistence.dart';
-import '../../services/global_widget.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import '../../widget/message_line.dart';
-import '../../widget/widget_form.dart';
-import 'edit_structure_page.dart';
-
-final i18n = I18N();
-
-class EditStructureCustom extends State<EditStructurePage> with MessageLine {
-  final int primaryKey;
-  final globalData = GlobalData();
-  late Future<DbData> _futureDbData;
-  late AttendedPage attendedPage;
-  final _fieldData = _FieldData();
-  final GlobalKey<FormState> _formKey =
-      GlobalKey<FormState>(debugLabel: 'EditStructure');
-  final scopeController = TextEditingController();
-  final nameController = TextEditingController();
-  final valueController = TextEditingController();
-  final positionController = TextEditingController();
-  EditStructureCustom(this.primaryKey);
-  @override
-  Widget build(BuildContext context) {
-    final rc = Scaffold(
-        appBar: globalData.appBarBuilder(i18n.tr('Change Structure')),
-        drawer: globalData.drawerBuilder(context),
-        body: SafeArea(
-            child: FutureBuilder<DbData>(
-                future: _futureDbData,
-                builder: (context, snapshot) {
-                  final rc = attendedPage.loadRecord(snapshot, (record) {
-                    _fieldData.fromMap(record);
-                    return buildFrame();
-                  });
-                  return rc;
-                })));
-    return rc;
-  }
-
-  Widget buildFrame() {
-    final padding = GlobalThemeData.padding;
-    scopeController.text = _fieldData.scope;
-    nameController.text = _fieldData.name;
-    valueController.text = _fieldData.value;
-    positionController.text = asString(_fieldData.position);
-    final formItems = <FormItem>[
-      FormItem(
-          TextFormField(
-              controller: scopeController,
-              decoration: InputDecoration(labelText: i18n.tr('Scope')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.scope = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: nameController,
-              decoration: InputDecoration(labelText: i18n.tr('Name')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.name = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: valueController,
-              decoration: InputDecoration(labelText: i18n.tr('Value')),
-              validator: (input) => notEmpty(input),
-              onSaved: (value) => _fieldData.value = value ?? ''),
-          weight: 6),
-      FormItem(
-          TextFormField(
-              controller: positionController,
-              decoration: InputDecoration(labelText: i18n.tr('Position')),
-              onSaved: (value) => _fieldData.position =
-                  jsonToObject(value ?? '', dataType: DataType.int)),
-          weight: 6),
-      FormItem(
-          ElevatedButton(
-              onPressed: () => onVerifyAndStore(),
-              child: Text(i18n.tr('Save'))),
-          weight: 8,
-          gapAbove: 2 * padding),
-      FormItem(
-          ElevatedButton(
-            onPressed: () {
-              attendedPage.pageStates.dbDataState.clear();
-              globalData.navigate(context, '/Structures/list');
-            },
-            child: Text(i18n.tr('Cancel')),
-          ),
-          weight: 4)
-    ];
-    final rc = Form(
-        key: _formKey,
-        child: Card(
-            margin:
-                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
-            child: Padding(
-                padding: EdgeInsets.symmetric(
-                    vertical: padding, horizontal: padding),
-                child: WidgetForm.flexibleGrid(
-                  formItems,
-                  screenWidth: attendedPage.pageStates.screenWidth,
-                  padding: padding,
-                ))));
-    return rc;
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    requestRecord();
-  }
-
-  @override
-  void dispose() {
-    helperDummyUsage(DataType.int);
-    globalWidgetDummyUsage();
-    scopeController.dispose();
-    nameController.dispose();
-    valueController.dispose();
-    positionController.dispose();
-    super.dispose();
-  }
-
-  @override
-  void initState() {
-    super.initState();
-  }
-
-  void requestRecord() => _futureDbData = globalData.restPersistence.query(
-      what: 'query',
-      data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey});
-
-  void onStore() {
-    final parameters = <String, dynamic>{
-      'module': 'Structures',
-      'sql': 'update',
-      ':id': primaryKey
-    };
-    _fieldData.toMap(parameters);
-    globalData.restPersistence
-        .store(what: 'store', map: parameters)
-        .then((answer) {
-      requestRecord();
-      attendedPage.pageStates.dbDataState.clear();
-      setState(() => 1);
-    });
-  }
-
-  void onVerifyAndStore() {
-    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
-      _formKey.currentState!.save();
-      onStore();
-    }
-  }
-}
-
-class _FieldData {
-  String scope = '';
-  String name = '';
-  String value = '';
-  int position = 0;
-
-  void fromMap(Map<String, dynamic> map) {
-    scope = map['structure_scope'];
-    name = map['structure_name'];
-    value = map['structure_value'];
-    position = jsonToObject(map['structure_position'], dataType: DataType.int);
-  }
-
-  void toMap(Map<String, dynamic> map) {
-    // please set outside: map[':id'] = primaryKey;
-    map[':scope'] = scope;
-    map[':name'] = name;
-    map[':value'] = value;
-    map[':position'] = asString(position, dbFormat: true);
-    map[':changedBy'] = GlobalData.loginUserName;
-  }
-}
diff --git a/lib/page/structures/edit_structure_page.dart b/lib/page/structures/edit_structure_page.dart
deleted file mode 100644 (file)
index 5c4ef5f..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// DO NOT CHANGE. This file is created by the meta_tool!
-import 'package:flutter/material.dart';
-
-import '../../meta/structures_meta.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import 'edit_structure_custom.dart';
-
-class EditStructurePage extends StatefulWidget {
-  final int primaryKey;
-  final PageStates pageStates = PageStates();
-  EditStructurePage(this.primaryKey) : super();
-  @override
-  _EditStructurePageState createState() {
-    final rc = _EditStructurePageState(this.primaryKey);
-    rc.attendedPage = AttendedPage(
-        this,
-        rc,
-        GlobalData(),
-        StructuresMeta.instance.pageByName('edit')!,
-        StructuresMeta.instance,
-        pageStates,
-        (afterReload, rebuild) =>
-            rc.reload(afterReload: afterReload, rebuild: rebuild));
-    pageStates.attendedPage = rc.attendedPage;
-    return rc;
-  }
-}
-
-class _EditStructurePageState extends EditStructureCustom {
-  _EditStructurePageState(int primaryKey) : super(primaryKey);
-  @override
-  void didChangeDependencies() {
-    final size = MediaQuery.of(context).size;
-    attendedPage.pageStates.screenWidth = size.width;
-    attendedPage.pageStates.screenHeight = size.height;
-    super.didChangeDependencies();
-  }
-
-  /// Renders the widget tree again.
-  ///
-  /// [afterReload] is a function used as parameter of setState().
-  ///
-  /// If [rebuild] is true the state has been changed and didChangeDependencies()
-  /// will be called.
-  void reload({Function? afterReload, bool rebuild = false}) {
-    if (afterReload == null) {
-      if (rebuild) {
-        setState(() => didChangeDependencies());
-      } else {
-        setState(() => 1);
-      }
-    } else {
-      setState(() {
-        afterReload();
-        if (rebuild) {
-          didChangeDependencies();
-        }
-      });
-    }
-  }
-}
diff --git a/lib/page/structures/list_structure_custom.dart b/lib/page/structures/list_structure_custom.dart
deleted file mode 100644 (file)
index 4cec578..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-// This file is created by the meta_tool. But it can be customized.
-// It will never overridden by the meta_tool.
-import 'package:flutter/material.dart';
-
-import '../../base/defines.dart';
-import '../../base/helper.dart';
-import '../../base/i18n.dart';
-import '../../services/global_widget.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import '../../widget/widget_form.dart';
-import '../../persistence/persistence.dart';
-import 'list_structure_page.dart';
-
-final i18n = I18N();
-
-class ListStructureCustom extends State<ListStructurePage> {
-  final globalData = GlobalData();
-  late Future<DbData> _futureDbData;
-  late AttendedPage attendedPage;
-  final _fieldData = _FieldData();
-  final GlobalKey<FormState> _formKey =
-      GlobalKey<FormState>(debugLabel: 'CreateStructure');
-  final textController = TextEditingController();
-  ListStructureCustom();
-  @override
-  Widget build(BuildContext context) {
-    final rc = Scaffold(
-      appBar: globalData.appBarBuilder(i18n.tr('Overview structures')),
-      drawer: globalData.drawerBuilder(context),
-      floatingActionButton: FloatingActionButton(
-          onPressed: () {
-            globalData.navigate(context, '/Structures/create');
-          },
-          child: const Icon(Icons.add),
-          tooltip: 'Add a structure item'),
-      body: SafeArea(
-          child: FutureBuilder<DbData>(
-        future: _futureDbData,
-        builder: (context, snapshot) {
-          Widget rc;
-          if (snapshot.connectionState != ConnectionState.done) {
-            rc = const CircularProgressIndicator();
-          } else {
-            if (snapshot.hasData) {
-              final rows = attendedPage.getRows(
-                  dbData: snapshot.data!,
-                  columnList:
-                      'structure_id;structure_scope;structure_name;structure_value;structure_position',
-                  onDone: () => setState(() => 1),
-                  routeEdit: '/Structures/edit',
-                  context: context);
-              rc = buildFrame(
-                  totalCount: snapshot.data?.count ?? rows.length, rows: rows);
-            } else if (snapshot.hasError) {
-              rc = Text('Backend problem: ${snapshot.error}');
-            } else {
-              rc = const CircularProgressIndicator();
-            }
-          }
-          return rc;
-        },
-      )),
-    );
-    return rc;
-  }
-
-  Widget buildFrame({required JsonList rows, required int totalCount}) {
-    final padding = GlobalThemeData.padding;
-    final formItems = <FormItem>[
-      FormItem(
-          TextFormField(
-              controller: textController,
-              decoration: InputDecoration(labelText: i18n.tr('Text')),
-              onSaved: (value) => _fieldData.text = value ?? ''),
-          weight: 6),
-      FormItem(
-          ElevatedButton(
-              onPressed: () => search(), child: Text(i18n.tr('Search'))),
-          weight: 12,
-          gapAbove: padding),
-    ];
-    final form = Form(
-        key: _formKey,
-        child: Card(
-            color: GlobalThemeData.formBackgroundColor,
-            elevation: GlobalThemeData.formElevation,
-            margin:
-                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
-            child: Padding(
-                padding: EdgeInsets.symmetric(
-                    vertical: padding, horizontal: padding),
-                child: WidgetForm.flexibleGrid(formItems,
-                    screenWidth: attendedPage.pageStates.screenWidth,
-                    padding: padding))));
-    final table = DataTable(
-      columns: <DataColumn>[
-        DataColumn(
-          label: Text(i18n.tr('Id')),
-        ),
-        DataColumn(
-          label: Text(i18n.tr('Scope')),
-        ),
-        DataColumn(
-          label: Text(i18n.tr('Name')),
-        ),
-        DataColumn(
-          label: Text(i18n.tr('Value')),
-        ),
-        DataColumn(
-          label: Text(i18n.tr('Position')),
-        ),
-      ],
-      rows: rows as List<DataRow>,
-    );
-    Widget? tabBar = attendedPage.buildChipBar(
-        totalCount: totalCount,
-        offset: _fieldData.theOffset,
-        pageSize: _fieldData.thePageSize,
-        onTap: (offset) {
-          _fieldData.theOffset = offset;
-          requestRecords();
-          setState(() => 1);
-        });
-    final frameWidget = ListView(children: [
-      form,
-      if (tabBar != null) tabBar,
-      SizedBox(height: padding),
-      SizedBox(width: double.infinity, child: table),
-    ]);
-    return frameWidget;
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    requestRecords();
-  }
-
-  @override
-  void dispose() {
-    helperDummyUsage(DataType.string);
-    globalWidgetDummyUsage();
-    textController.dispose();
-    super.dispose();
-  }
-
-  @override
-  void initState() {
-    super.initState();
-  }
-
-  void requestRecords() =>
-      _futureDbData = globalData.restPersistence.query(what: 'query', data: {
-        'module': 'Structures',
-        'sql': 'list',
-        'offset': _fieldData.theOffset,
-        'size': _fieldData.thePageSize,
-        ':text': _fieldData.text,
-      });
-
-  void search() {
-    attendedPage.pageStates.dbDataState.clear();
-    if (_formKey.currentState != null && _formKey.currentState!.validate()) {
-      _formKey.currentState!.save();
-      requestRecords();
-      setState(() => 1);
-    }
-  }
-}
-
-class _FieldData {
-  int thePageSize = 10;
-  int theOffset = 0;
-  String text = '';
-}
diff --git a/lib/page/structures/list_structure_page.dart b/lib/page/structures/list_structure_page.dart
deleted file mode 100644 (file)
index 4b6d716..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-// DO NOT CHANGE. This file is created by the meta_tool!
-import 'package:flutter/material.dart';
-
-import '../../meta/structures_meta.dart';
-import '../../setting/global_data.dart';
-import '../../widget/attended_page.dart';
-import 'list_structure_custom.dart';
-
-class ListStructurePage extends StatefulWidget {
-  final PageStates pageStates = PageStates();
-  ListStructurePage() : super();
-  @override
-  _ListStructurePageState createState() {
-    final rc = _ListStructurePageState();
-    rc.attendedPage = AttendedPage(
-        this,
-        rc,
-        GlobalData(),
-        StructuresMeta.instance.pageByName('list')!,
-        StructuresMeta.instance,
-        pageStates,
-        (afterReload, rebuild) =>
-            rc.reload(afterReload: afterReload, rebuild: rebuild));
-    pageStates.attendedPage = rc.attendedPage;
-    return rc;
-  }
-}
-
-class _ListStructurePageState extends ListStructureCustom {
-  _ListStructurePageState() : super();
-  @override
-  void didChangeDependencies() {
-    final size = MediaQuery.of(context).size;
-    attendedPage.pageStates.screenWidth = size.width;
-    attendedPage.pageStates.screenHeight = size.height;
-    super.didChangeDependencies();
-  }
-
-  /// Renders the widget tree again.
-  ///
-  /// [afterReload] is a function used as parameter of setState().
-  ///
-  /// If [rebuild] is true the state has been changed and didChangeDependencies()
-  /// will be called.
-  void reload({Function? afterReload, bool rebuild = false}) {
-    if (afterReload == null) {
-      if (rebuild) {
-        setState(() => didChangeDependencies());
-      } else {
-        setState(() => 1);
-      }
-    } else {
-      setState(() {
-        afterReload();
-        if (rebuild) {
-          didChangeDependencies();
-        }
-      });
-    }
-  }
-}
diff --git a/lib/page/structures/structure_data.dart b/lib/page/structures/structure_data.dart
deleted file mode 100644 (file)
index c5d8586..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-// DO NOT CHANGE. This file is created by the meta_tool
-import '../../base/defines.dart';
-import '../../base/helper.dart';
-import '../../persistence/data_record.dart';
-
-class StructureData extends DataRecord<int> {
-  int? id;
-  String? scope;
-  String? name;
-  String? value;
-  int? position;
-  DateTime? createdAt;
-  String? createdBy;
-  DateTime? changedAt;
-  String? changedBy;
-  StructureData(
-      {this.id,
-      this.scope,
-      this.name,
-      this.value,
-      this.position,
-      this.createdAt,
-      this.createdBy,
-      this.changedAt,
-      this.changedBy});
-  StructureData.createFromMap(DataMap map) {
-    fromMap(map);
-  }
-  @override
-  void fromMap(DataMap map) {
-    id = map.containsKey('structure_id')
-        ? fromString(map['structure_id'], dataType: DataType.reference)
-        : null;
-    scope = map.containsKey('structure_scope')
-        ? fromString(map['structure_scope'], dataType: DataType.string)
-        : null;
-    name = map.containsKey('structure_name')
-        ? fromString(map['structure_name'], dataType: DataType.string)
-        : null;
-    value = map.containsKey('structure_value')
-        ? fromString(map['structure_value'], dataType: DataType.string)
-        : null;
-    position = map.containsKey('structure_position')
-        ? fromString(map['structure_position'], dataType: DataType.int)
-        : null;
-    createdAt = map.containsKey('structure_createdat')
-        ? fromString(map['structure_createdat'], dataType: DataType.datetime)
-        : null;
-    createdBy = map.containsKey('structure_createdby')
-        ? fromString(map['structure_createdby'], dataType: DataType.string)
-        : null;
-    changedAt = map.containsKey('structure_changedat')
-        ? fromString(map['structure_changedat'], dataType: DataType.datetime)
-        : null;
-    changedBy = map.containsKey('structure_changedby')
-        ? fromString(map['structure_changedby'], dataType: DataType.string)
-        : null;
-  }
-
-  @override
-  int keyOf() {
-    return id ?? 0;
-  }
-
-  @override
-  String nameOfKey() {
-    return 'structure_id';
-  }
-
-  static DataType? dataTypeOf(String name) {
-    DataType? rc;
-    switch (name) {
-      case 'id':
-        rc = DataType.reference;
-        break;
-      case 'scope':
-        rc = DataType.string;
-        break;
-      case 'name':
-        rc = DataType.string;
-        break;
-      case 'value':
-        rc = DataType.string;
-        break;
-      case 'position':
-        rc = DataType.int;
-        break;
-      case 'createdAt':
-        rc = DataType.datetime;
-        break;
-      case 'createdBy':
-        rc = DataType.string;
-        break;
-      case 'changedAt':
-        rc = DataType.datetime;
-        break;
-      case 'changedBy':
-        rc = DataType.string;
-        break;
-      default:
-        break;
-    }
-    return rc;
-  }
-
-  @override
-  DataMap toMap({DataMap? map, bool clear = true}) {
-    map ??= DataMap();
-    if (clear) {
-      map.clear();
-    }
-    map['structure_id'] = id;
-    map['structure_scope'] = scope;
-    map['structure_name'] = name;
-    map['structure_value'] = value;
-    map['structure_position'] = position;
-    map['structure_createdat'] = createdAt;
-    map['structure_createdby'] = createdBy;
-    map['structure_changedat'] = changedAt;
-    map['structure_changedby'] = changedBy;
-    return map;
-  }
-}
index 9428ee756ed0e88059468566b00134570708df95..4d6f4addd1d1fe1abfdc2e3611afcc0605f874e4 100644 (file)
@@ -28,6 +28,7 @@ class CreateUserPage extends StatefulWidget {
 
 class _CreateUserPageState extends CreateUserCustom {
   _CreateUserPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _CreateUserPageState extends CreateUserCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 5b627576e2b59de43ca4c795b5382d70c442070a..91c796855c099c4082d943096c7fa2c622c2efe9 100644 (file)
@@ -29,6 +29,7 @@ class DeleteUserPage extends StatefulWidget {
 
 class _DeleteUserPageState extends DeleteUserCustom {
   _DeleteUserPageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _DeleteUserPageState extends DeleteUserCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 18ae325a906338e7afa7c20051909cb4e46bd2ec..53783651a5da7803a971d7ed6b6345c848071622 100644 (file)
@@ -29,6 +29,7 @@ class EditUserPage extends StatefulWidget {
 
 class _EditUserPageState extends EditUserCustom {
   _EditUserPageState(int primaryKey) : super(primaryKey);
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -59,4 +60,4 @@ class _EditUserPageState extends EditUserCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index b4f014c39aaa74367ebe640b02712fc0d3755837..a83638ebeed3c40986197d4d8fb5c34ffba06ab0 100644 (file)
@@ -28,6 +28,7 @@ class ListUserPage extends StatefulWidget {
 
 class _ListUserPageState extends ListUserCustom {
   _ListUserPageState() : super();
+
   @override
   void didChangeDependencies() {
     final size = MediaQuery.of(context).size;
@@ -58,4 +59,4 @@ class _ListUserPageState extends ListUserCustom {
       });
     }
   }
-}
+}
\ No newline at end of file
index 61a9eea5b17da750ea7aa5ed8b9f29feeafdbe0a..7cac062933b0fc6e955850fce53ca92007fa0151 100644 (file)
@@ -2,30 +2,34 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-
 class UserData extends DataRecord<int> {
   int? id;
   String? name;
   String? displayName;
   String? email;
   int? role;
+  int? status;
   DateTime? createdAt;
   String? createdBy;
   DateTime? changedAt;
   String? changedBy;
+
   UserData(
       {this.id,
       this.name,
       this.displayName,
       this.email,
       this.role,
+      this.status,
       this.createdAt,
       this.createdBy,
       this.changedAt,
       this.changedBy});
+
   UserData.createFromMap(DataMap map) {
     fromMap(map);
   }
+
   @override
   void fromMap(DataMap map) {
     id = map.containsKey('user_id')
@@ -43,6 +47,9 @@ class UserData extends DataRecord<int> {
     role = map.containsKey('user_role')
         ? fromString(map['user_role'], dataType: DataType.reference)
         : null;
+    status = map.containsKey('user_status')
+        ? fromString(map['user_status'], dataType: DataType.reference)
+        : null;
     createdAt = map.containsKey('user_createdat')
         ? fromString(map['user_createdat'], dataType: DataType.datetime)
         : null;
@@ -85,6 +92,9 @@ class UserData extends DataRecord<int> {
       case 'role':
         rc = DataType.reference;
         break;
+      case 'status':
+        rc = DataType.reference;
+        break;
       case 'createdAt':
         rc = DataType.datetime;
         break;
@@ -102,7 +112,6 @@ class UserData extends DataRecord<int> {
     }
     return rc;
   }
-
   @override
   DataMap toMap({DataMap? map, bool clear = true}) {
     map ??= DataMap();
@@ -114,6 +123,7 @@ class UserData extends DataRecord<int> {
     map['user_displayname'] = displayName;
     map['user_email'] = email;
     map['user_role'] = role;
+    map['user_status'] = status;
     map['user_createdat'] = createdAt;
     map['user_createdby'] = createdBy;
     map['user_changedat'] = changedAt;
index 8449761e7d9e318e2bf8c074a9796672e183f4bb..c557efd9a180f92da91b55b4bd35d2f2b6b990e1 100644 (file)
@@ -32,6 +32,32 @@ void comboRolesFromBackend(
       {'module': 'global', 'sql': 'comboRoles'}, onDone);
 }
 
+/// Returns the combobox items filled with data of the table users.
+///
+/// [textUndefined]: null: no additional entry. Otherwise: a additional entry is
+/// inserted at position 0 with this text and the value 0.
+///
+/// [attendedPage]: the context of the calling page.
+///
+/// Returns a list of combobox items selecting a user.
+List<DropdownMenuItem<int>>? comboUsers(
+    String textUndefined, AttendedPage attendedPage) {
+  final rc = _combo('comboUsers.global', 'user_id', 'user_displayname',
+      textUndefined, attendedPage);
+  return rc;
+}
+
+/// Requests the database data for combo boxes from the backend.
+///
+/// [dbDataState]: Stores the request and the answer of the request.
+///
+/// [onDone]: A method executed after arriving the response.
+void comboUsersFromBackend(
+    {required AttendedPage attendedPage, required Function() onDone}) {
+  _requestData('comboUsers.global', attendedPage,
+      {'module': 'global', 'sql': 'comboActiveUsers'}, onDone);
+}
+
 /// This function is only used to avoid compiler warning "unused import"
 /// for generated code.
 void globalWidgetDummyUsage() {
index 43dd5c890c789ce163123e5199c0ebc8e2e562d8..00e3ff2108728eea084c715d60ba6dbc340456fb 100755 (executable)
@@ -1,4 +1,4 @@
 #! /bin/bash
 APP=meta_tool
 OUT=$(dirname $(pwd))/tools
-/usr/bin/dart compile exe bin/$APP.dart -o $OUT/$APP
+dart compile exe bin/$APP.dart -o $OUT/$APP
index 435a75a9c17f877587e5d5db02d2d7b9b5840516..41978d5c58ada2afa21c407da6891abf1271cd51 100644 (file)
@@ -72,9 +72,9 @@ msgstr ""
     content = fileSync.fileAsString(fn);
     expect(content.replaceAll(RegExp(r'Date: 2[-+\w2: ]+'), 'Date: *'),
         r'''# Texts of module Statistic, created by i18n_text_parser');
-# Copyright (C) 2021-2021 J. Hamatoma <cr@hamatoma.de>
+# Copyright (C) 2021-2022 J. Hamatoma <cr@hamatoma.de>
 # License: CC0 1.0 Universal
-# J. Hamatoma <author@hamatoma.de>, 2021.
+# J. Hamatoma <author@hamatoma.de>, 2022.
 #
 #, fuzzy
 msgid ""
@@ -104,9 +104,9 @@ msgstr[1] ""
     final content = fileSync.fileAsString(fn);
     expect(content.replaceAll(RegExp(r'Date: 2[-+\w2: ]+'), 'Date: *'),
         r'''# Texts of module Statistic, created by i18n_text_parser');
-# Copyright (C) 2021-2021 J. Hamatoma <cr@hamatoma.de>
+# Copyright (C) 2021-2022 J. Hamatoma <cr@hamatoma.de>
 # License: CC0 1.0 Universal
-# J. Hamatoma <author@hamatoma.de>, 2021.
+# J. Hamatoma <author@hamatoma.de>, 2022.
 #
 #, fuzzy
 msgid ""
diff --git a/test/validators_test.dart b/test/validators_test.dart
new file mode 100644 (file)
index 0000000..a7c7a48
--- /dev/null
@@ -0,0 +1,62 @@
+import 'package:dart_bones/dart_bones.dart';
+import 'package:exhibition/base/i18n.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../lib/base/validators.dart';
+
+I18N i18n = I18N.internal(MemoryLogger());
+
+void main() {
+  final logger = MemoryLogger(LEVEL_FINE);
+  i18n = I18N.internal(logger);
+  test('isEmail', () {
+    expect(isEmail('info@hamatoma.de'), isNull);
+    expect(isEmail('/info@hamatoma.de'), matches(RegExp('"/"')));
+    expect(isEmail('info@hamatoma,de'), matches(RegExp('","')));
+    expect(isEmail('info@hamatoma'), isNotNull);
+    expect(isEmail('x@info@hamatoma'), isNotNull);
+    expect(isEmail('x@info@hamatoma.de'), matches(RegExp('"@"')));
+  });
+  test('isInt', () {
+    expect(isInt('1234'), isNull);
+    expect(isInt('0'), isNull);
+    expect(isInt('-330'), isNull);
+    expect(isInt('+330'), isNull);
+    expect(isInt('.123'), isNotNull);
+    expect(isInt('3D'), isNotNull);
+    expect(isInt(''), isNotNull);
+    expect(isInt(null), isNotNull);
+  });
+  test('isNat', () {
+    expect(isNat('1234'), isNull);
+    expect(isNat('0'), isNull);
+    expect(isNat('-330'), isNotNull);
+    expect(isNat('+330'), isNotNull);
+    expect(isNat('.123'), isNotNull);
+    expect(isNat('3D'), isNotNull);
+    expect(isNat(''), isNotNull);
+    expect(isNat(null), isNotNull);
+  });
+  test('isTime', () {
+    expect(isTime('0'), isNull);
+    expect(isTime('1'), isNull);
+    expect(isTime('23'), isNull);
+    expect(isTime('24'), matches('24'));
+    expect(isTime('00:00'), isNull);
+    expect(isTime('00:59'), isNull);
+    expect(isTime('0:3'), isNull);
+    expect(isTime('0:39'), isNull);
+    expect(isTime('23:59'), isNull);
+    expect(isTime('23:59'), isNull);
+    expect(isTime(''), isNotNull);
+    expect(isTime(null), isNotNull);
+    expect(isTime(null, mayBeEmpty: true), isNull);
+    expect(isTime('', mayBeEmpty: true), isNull);
+  });
+  test('notEmpty', () {
+    expect(notEmpty('0'), isNull);
+    expect(notEmpty('abc'), isNull);
+    expect(notEmpty(''), isNotNull);
+    expect(notEmpty(null), isNotNull);
+  });
+}