From: Hamatoma Date: Tue, 12 Oct 2021 20:22:58 +0000 (+0200) Subject: meta_tool generates correct list, edit, create and delete customer files X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=8294d218dd43b1b7b623533ba59eb521a64033d0;p=exhibition.git meta_tool generates correct list, edit, create and delete customer files --- diff --git a/lib/meta/module_meta_data.dart b/lib/meta/module_meta_data.dart index b7a105d..c6a237f 100644 --- a/lib/meta/module_meta_data.dart +++ b/lib/meta/module_meta_data.dart @@ -12,6 +12,11 @@ class ButtonMetaData extends WidgetMetaData { } } +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; @@ -22,10 +27,10 @@ class DbFieldMetaData extends WidgetMetaData { } enum DisplayType { - text, - combobox, checkbox, + combobox, custom, + text, } class DummyModule extends ModuleMetaData { @@ -42,6 +47,19 @@ class FieldMetaData extends WidgetMetaData { : super(name, WidgetType.field); } +class ListPageMetaData extends PageMetaData { + final String tableHeaders; + final String tableColumns; + ListPageMetaData(String label, + {String name = '', + required List fields, + required this.tableColumns, + required this.tableHeaders, + String globalComboBoxes = ''}) + : super(label, PageType.list, + name: name, fields: fields, globalComboBoxes: globalComboBoxes); +} + class MetaException extends FormatException {} /// Stores the meta data of a module. @@ -207,7 +225,9 @@ class ModuleMetaData { /// /// Override it if needed. void onInitialized() { - // do nothing + for (var page in this.pageList){ + page.onInitialized(); + } } /// Returns the meta data of a page given by its [name] or null if missing. @@ -247,21 +267,51 @@ class PageMetaData { final String label; final PageType pageType; ModuleMetaData module = DummyModule(); - final List? field = []; - PageMetaData(this.label, this.pageType, {String name = ''}) { + final List fields; + final String globalComboBoxes; + PageMetaData(this.label, this.pageType, + {String name = '', required this.fields, this.globalComboBoxes = ''}) { if (name.isEmpty) { name = enumToString(pageType); } this.name = name; } + void onInitialized(){ + var newFields = []; + var toDelete = []; + 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 } /// Stores the meta data of a module property stored as column in a database. -class PropertyMetaData { - /// Name of the property (Dart name). - final String name; +class PropertyMetaData extends WidgetMetaData { ModuleMetaData module = DummyModule(); final String label; @@ -277,16 +327,18 @@ class PropertyMetaData { /// The size if dataType is DataType.string. final int size; String columnName; - + var validators = []; /// The foreign key if dataType is DataType.reference, e.g. 'users.user_id' String? foreignKey; - PropertyMetaData(this.name, this.label, this.dataType, this.options, + PropertyMetaData(String name, this.label, this.dataType, this.options, {this.displayType = DisplayType.text, this.columnName = '', this.size = 0, this.foreignKey, - int weight = 6}) { + int weight = 6, List? validators}) + : super(name, WidgetType.property) { this.weight = weight > 12 ? 12 : weight; + this.validators = validators ?? []; } /// Returns whether a given [option] is set. @@ -307,4 +359,4 @@ class WidgetMetaData { WidgetMetaData(this.name, this.widgetType); } -enum WidgetType { button, field, dbField } +enum WidgetType { button, field, dbField, property } diff --git a/lib/meta/modules.dart b/lib/meta/modules.dart index 2ff718c..5d0d3ed 100644 --- a/lib/meta/modules.dart +++ b/lib/meta/modules.dart @@ -3,7 +3,6 @@ import 'module_meta_data.dart'; import 'roles_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) { @@ -23,9 +22,8 @@ ModuleMetaData? moduleByName(String name) { } return rc; } - /// Returns the module names as string list. -List moduleNames() { +List moduleNames(){ return [ 'Roles', 'Structures', diff --git a/lib/meta/roles_meta.dart b/lib/meta/roles_meta.dart index 7e1225a..e91e3fc 100644 --- a/lib/meta/roles_meta.dart +++ b/lib/meta/roles_meta.dart @@ -28,10 +28,23 @@ class RoleMeta extends ModuleMetaData { 'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:', size: 32), ], [ - PageMetaData('New Role', PageType.create), - PageMetaData('Change Role', PageType.edit), - PageMetaData('Roles Overview', PageType.list), + PageMetaData('New Role', PageType.create, + fields: [CopyDbFields('filters')]), + PageMetaData('Change Role', PageType.edit, + fields: [CopyDbFields('filters')]), + ListPageMetaData( + 'Roles Overview', + fields: [ + PropertyMetaData('text', i18n.tr('Text'), DataType.string, + ':where=!text IS NONE OR (role_name like !text):', + size: 64), + ], + tableColumns: 'role_id;role_name', + tableHeaders: i18n.tr('Id;Name'), + ), ]); @override - void onInitialized() {} + void onInitialized() { + super.onInitialized(); + } } diff --git a/lib/meta/structures_meta.dart b/lib/meta/structures_meta.dart index 8f0f5e7..7bfccdc 100644 --- a/lib/meta/structures_meta.dart +++ b/lib/meta/structures_meta.dart @@ -36,9 +36,26 @@ class StructureMeta extends ModuleMetaData { 'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:', size: 32), ], [ - PageMetaData('New Structure', PageType.create), - PageMetaData('Change Structure', PageType.edit), - PageMetaData('Delete Structure', PageType.delete), - PageMetaData('Structures Overview', PageType.list), + PageMetaData('New Structure', PageType.create, + fields: [CopyDbFields('filters')]), + PageMetaData('Change Structure', PageType.edit, + fields: [CopyDbFields('filters')]), + PageMetaData('Delete Structure', PageType.delete, + fields: [CopyDbFields('filters')]), + 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), + ], + tableColumns: + 'structure_id;structure_scope;structure_name;structure_value;structure_position', + tableHeaders: i18n.tr('Id;Scope;Name;Value;Position'), + ), ]); + @override + void onInitialized() { + super.onInitialized(); + } } diff --git a/lib/meta/users_meta.dart b/lib/meta/users_meta.dart index 0eba0ff..9b396d7 100644 --- a/lib/meta/users_meta.dart +++ b/lib/meta/users_meta.dart @@ -22,7 +22,7 @@ class UserMeta extends ModuleMetaData { size: 32), PropertyMetaData( 'email', i18n.tr('EMail', M), DataType.string, ':unique:notnull:', - size: 255), + size: 255, validators: ['isEmail(input)']), PropertyMetaData( 'role', i18n.tr('Role'), DataType.reference, ':notnull:', displayType: DisplayType.combobox, @@ -38,9 +38,40 @@ class UserMeta extends ModuleMetaData { 'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:', size: 32), ], [ - PageMetaData('New User', PageType.create), - PageMetaData('Change User', PageType.edit), - PageMetaData('Delete User', PageType.delete), - PageMetaData('Users Overview', PageType.list), + PageMetaData( + 'New User', + PageType.create, + fields: [CopyDbFields('filters')], + globalComboBoxes: 'comboRoles', + ), + PageMetaData( + 'Change User', + PageType.edit, + fields: [CopyDbFields('filters')], + globalComboBoxes: 'comboRoles', + ), + PageMetaData( + 'Delete User', + PageType.delete, + fields: [CopyDbFields('filters')], + globalComboBoxes: 'comboRoles', + ), + ListPageMetaData( + 'Users Overview', + fields: [ + PropertyMetaData('text', i18n.tr('Text'), DataType.string, '', + size: 64), + PropertyMetaData('role', i18n.tr('Role'), DataType.reference, '', + displayType: DisplayType.combobox, + foreignKey: 'roles.role_id;role_name;role'), + ], + tableColumns: 'user_id;user_displayname;user_email;role', + tableHeaders: i18n.tr('Id;Display Name;Email;Role'), + globalComboBoxes: 'comboRoles', + ), ]); + @override + void onInitialized() { + super.onInitialized(); + } } diff --git a/lib/page/roles/create_role_page.dart b/lib/page/roles/create_role_page.dart index 2cd8530..4ec6c88 100644 --- a/lib/page/roles/create_role_page.dart +++ b/lib/page/roles/create_role_page.dart @@ -20,7 +20,7 @@ class CreateRolePage extends StatefulWidget { } class _CreateRolePageState extends CreateRoleCustom { - _CreateRolePageState() : super(); + _CreateRolePageState(): super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/roles/edit_role_page.dart b/lib/page/roles/edit_role_page.dart index 6c32f01..ef15ac4 100644 --- a/lib/page/roles/edit_role_page.dart +++ b/lib/page/roles/edit_role_page.dart @@ -21,7 +21,7 @@ class EditRolePage extends StatefulWidget { } class _EditRolePageState extends EditRoleCustom { - _EditRolePageState(int primaryKey) : super(primaryKey); + _EditRolePageState(int primaryKey): super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/roles/list_role_page.dart b/lib/page/roles/list_role_page.dart index 0915e10..b42f052 100644 --- a/lib/page/roles/list_role_page.dart +++ b/lib/page/roles/list_role_page.dart @@ -20,7 +20,7 @@ class ListRolePage extends StatefulWidget { } class _ListRolePageState extends ListRoleCustom { - _ListRolePageState() : super(); + _ListRolePageState(): super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/structures/create_structure_page.dart b/lib/page/structures/create_structure_page.dart index e27c01e..88aa0fb 100644 --- a/lib/page/structures/create_structure_page.dart +++ b/lib/page/structures/create_structure_page.dart @@ -12,15 +12,15 @@ class CreateStructurePage extends StatefulWidget { @override _CreateStructurePageState createState() { final rc = _CreateStructurePageState(); - rc.attendedPage = AttendedPage( - this, rc, GlobalData(), StructureMeta.instance, pageStates); + rc.attendedPage = + AttendedPage(this, rc, GlobalData(), StructureMeta.instance, pageStates); pageStates.attendedPage = rc.attendedPage; return rc; } } class _CreateStructurePageState extends CreateStructureCustom { - _CreateStructurePageState() : super(); + _CreateStructurePageState(): super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/structures/delete_structure_page.dart b/lib/page/structures/delete_structure_page.dart index cc888a8..2bdedf3 100644 --- a/lib/page/structures/delete_structure_page.dart +++ b/lib/page/structures/delete_structure_page.dart @@ -13,15 +13,15 @@ class DeleteStructurePage extends StatefulWidget { @override _DeleteStructurePageState createState() { final rc = _DeleteStructurePageState(this.primaryKey); - rc.attendedPage = AttendedPage( - this, rc, GlobalData(), StructureMeta.instance, pageStates); + rc.attendedPage = + AttendedPage(this, rc, GlobalData(), StructureMeta.instance, pageStates); pageStates.attendedPage = rc.attendedPage; return rc; } } class _DeleteStructurePageState extends DeleteStructureCustom { - _DeleteStructurePageState(int primaryKey) : super(primaryKey); + _DeleteStructurePageState(int primaryKey): super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/structures/edit_structure_page.dart b/lib/page/structures/edit_structure_page.dart index a689d34..c151f66 100644 --- a/lib/page/structures/edit_structure_page.dart +++ b/lib/page/structures/edit_structure_page.dart @@ -13,15 +13,15 @@ class EditStructurePage extends StatefulWidget { @override _EditStructurePageState createState() { final rc = _EditStructurePageState(this.primaryKey); - rc.attendedPage = AttendedPage( - this, rc, GlobalData(), StructureMeta.instance, pageStates); + rc.attendedPage = + AttendedPage(this, rc, GlobalData(), StructureMeta.instance, pageStates); pageStates.attendedPage = rc.attendedPage; return rc; } } class _EditStructurePageState extends EditStructureCustom { - _EditStructurePageState(int primaryKey) : super(primaryKey); + _EditStructurePageState(int primaryKey): super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/structures/list_structure_page.dart b/lib/page/structures/list_structure_page.dart index 55c901c..e286fbc 100644 --- a/lib/page/structures/list_structure_page.dart +++ b/lib/page/structures/list_structure_page.dart @@ -12,15 +12,15 @@ class ListStructurePage extends StatefulWidget { @override _ListStructurePageState createState() { final rc = _ListStructurePageState(); - rc.attendedPage = AttendedPage( - this, rc, GlobalData(), StructureMeta.instance, pageStates); + rc.attendedPage = + AttendedPage(this, rc, GlobalData(), StructureMeta.instance, pageStates); pageStates.attendedPage = rc.attendedPage; return rc; } } class _ListStructurePageState extends ListStructureCustom { - _ListStructurePageState() : super(); + _ListStructurePageState(): super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/users/create_user_custom.dart b/lib/page/users/create_user_custom.dart index 99296b1..ec8d94e 100644 --- a/lib/page/users/create_user_custom.dart +++ b/lib/page/users/create_user_custom.dart @@ -19,31 +19,32 @@ class CreateUserCustom extends State with MessageLine { final _fieldData = _FieldData(); final GlobalKey _formKey = GlobalKey(debugLabel: 'CreateUser'); + final nameController = TextEditingController(); + final displayNameController = TextEditingController(); + final emailController = TextEditingController(); CreateUserCustom(); @override Widget build(BuildContext context) { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); - final itemsRole = comboRoles( + final itemsRoles = comboRoles( i18n.trDyn(GlobalTranslations.comboboxSelect), attendedPage!); + nameController.text = _fieldData.name; + displayNameController.text = _fieldData.displayName; + emailController.text = _fieldData.email; final formItems = [ FormItem( TextFormField( - initialValue: _fieldData.name, - style: TextStyle( - backgroundColor: GlobalThemeData.formTextBackgroundColor, - decorationStyle: TextDecorationStyle.double), - decoration: InputDecoration( - labelText: i18n.tr('Name'), - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), validator: (input) => notEmpty(input), onSaved: (value) => _fieldData.name = value ?? '', ), weight: 6), FormItem( TextFormField( - initialValue: _fieldData.displayName, + controller: displayNameController, decoration: InputDecoration(labelText: i18n.tr('Display name')), validator: (input) => notEmpty(input), onSaved: (value) => _fieldData.displayName = value ?? '', @@ -51,17 +52,16 @@ class CreateUserCustom extends State with MessageLine { weight: 6), FormItem( TextFormField( - initialValue: _fieldData.email, - decoration: InputDecoration(labelText: i18n.tr('Email')), - validator: (input) => validateMultiple( - input, [(input) => notEmpty(input), (input) => isEmail(input)]), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), onSaved: (value) => _fieldData.email = value ?? '', ), weight: 6), FormItem( DropdownButtonFormField( value: _fieldData.role, - items: itemsRole, + items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), onChanged: (value) => _fieldData.role = value ?? 0, @@ -71,10 +71,11 @@ class CreateUserCustom extends State with MessageLine { ElevatedButton( onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, - gapAbove: padding), + gapAbove: 2 * padding), FormItem( ElevatedButton( onPressed: () { + attendedPage!.pageStates.dbDataState.clear(); globalData.navigate(context, '/Users/list'); }, child: Text(i18n.tr('Cancel')), @@ -82,22 +83,22 @@ class CreateUserCustom extends State with MessageLine { weight: 4) ]; final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Create an User')), + appBar: globalData.appBarBuilder(i18n.tr('New User')), drawer: globalData.drawerBuilder(context), body: SafeArea( - child: Card( - color: GlobalThemeData.formBackgroundColor, - elevation: GlobalThemeData.formElevation, - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( + child: Form( + key: _formKey, + child: Card( + margin: EdgeInsets.symmetric( vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - ))))); + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))))); return rc; } @@ -121,6 +122,7 @@ class CreateUserCustom extends State with MessageLine { setError(i18n.tr('Saving data failed: $answer')); setState(() => 1); } else { + attendedPage!.pageStates.dbDataState.clear(); globalData.navigate(context, '/Users/edit;$id'); } } @@ -133,6 +135,14 @@ class CreateUserCustom extends State with MessageLine { store(); } } + + @override + void dispose() { + nameController.dispose(); + displayNameController.dispose(); + emailController.dispose(); + super.dispose(); + } } class _FieldData { @@ -140,6 +150,7 @@ class _FieldData { String displayName = ''; String email = ''; int role = 0; + void toMap(Map map) { map[':name'] = name; map[':displayName'] = displayName; diff --git a/lib/page/users/create_user_page.dart b/lib/page/users/create_user_page.dart index 71a2c05..14e69e2 100644 --- a/lib/page/users/create_user_page.dart +++ b/lib/page/users/create_user_page.dart @@ -20,7 +20,7 @@ class CreateUserPage extends StatefulWidget { } class _CreateUserPageState extends CreateUserCustom { - _CreateUserPageState() : super(); + _CreateUserPageState(): super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/users/delete_user_custom.dart b/lib/page/users/delete_user_custom.dart index 7ab1bce..8415fb2 100644 --- a/lib/page/users/delete_user_custom.dart +++ b/lib/page/users/delete_user_custom.dart @@ -3,30 +3,108 @@ import 'package:flutter/material.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 'delete_user_page.dart'; final i18n = I18N(); -class DeleteUserCustom extends State { +class DeleteUserCustom extends State with MessageLine { final int primaryKey; final globalData = GlobalData(); AttendedPage? attendedPage; - + final _fieldData = _FieldData(); + final GlobalKey _formKey = + GlobalKey(debugLabel: 'DeleteUser'); + final nameController = TextEditingController(); + final displayNameController = TextEditingController(); + final emailController = TextEditingController(); DeleteUserCustom(this.primaryKey); @override Widget build(BuildContext context) { final padding = GlobalThemeData.padding; + comboRolesFromBackend( + attendedPage: attendedPage!, onDone: () => setState(() => 1)); + final itemsRoles = comboRoles( + i18n.trDyn(GlobalTranslations.comboboxSelect), attendedPage!); + attendedPage?.loadRecord( + name: 'record', + reload: () => setState(() => 1), + onDone: (record) => _fieldData.fromMap(record), + parameters: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + nameController.text = _fieldData.name; + displayNameController.text = _fieldData.displayName; + emailController.text = _fieldData.email; + final formItems = [ + FormItem( + TextFormField( + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? '', + ), + weight: 6), + FormItem( + TextFormField( + controller: displayNameController, + decoration: InputDecoration(labelText: i18n.tr('Display name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.displayName = value ?? '', + ), + weight: 6), + FormItem( + TextFormField( + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? '', + ), + weight: 6), + FormItem( + DropdownButtonFormField( + value: _fieldData.role, + items: itemsRoles, + isExpanded: true, + decoration: InputDecoration(labelText: i18n.tr('Role')), + onChanged: (value) => _fieldData.role = value ?? 0, + ), + weight: 6), + FormItem( + ElevatedButton( + onPressed: () => verifyAndDelete(), child: Text(i18n.tr('Delete'))), + weight: 8, + gapAbove: 2 * padding), + FormItem( + ElevatedButton( + onPressed: () { + attendedPage!.pageStates.dbDataState.clear(); + globalData.navigate(context, '/Users/list'); + }, + child: Text(i18n.tr('Cancel')), + ), + weight: 4) + ]; final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change User data')), + appBar: globalData.appBarBuilder(i18n.tr('Delete User')), drawer: globalData.drawerBuilder(context), body: SafeArea( - child: WidgetForm.flexibleGridAttended( - attendedPage!.attendedWidgets(), - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding))); + child: 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; } @@ -34,4 +112,46 @@ class DeleteUserCustom extends State { void initState() { super.initState(); } + + void delete() { + final parameters = { + 'module': 'Users', + 'sql': 'delete', + }; + _fieldData.toMap(parameters); + globalData.restPersistence! + .store(what: 'store', map: parameters) + .then((answer) { + globalData.navigate(context, '/Users/list'); + }); + } + + void verifyAndDelete() { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + delete(); + } + } + + @override + void dispose() { + nameController.dispose(); + displayNameController.dispose(); + emailController.dispose(); + super.dispose(); + } +} + +class _FieldData { + String name = ''; + String displayName = ''; + String email = ''; + int role = 0; + + void fromMap(Map map) { + name = map['user_name']; + displayName = map['user_displayname']; + email = map['user_email']; + role = map['user_role']; + } } diff --git a/lib/page/users/delete_user_page.dart b/lib/page/users/delete_user_page.dart index 8bbef4c..85cbfc9 100644 --- a/lib/page/users/delete_user_page.dart +++ b/lib/page/users/delete_user_page.dart @@ -21,7 +21,7 @@ class DeleteUserPage extends StatefulWidget { } class _DeleteUserPageState extends DeleteUserCustom { - _DeleteUserPageState(int primaryKey) : super(primaryKey); + _DeleteUserPageState(int primaryKey): super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/users/edit_user_custom.dart b/lib/page/users/edit_user_custom.dart index bf6fc64..4727f34 100644 --- a/lib/page/users/edit_user_custom.dart +++ b/lib/page/users/edit_user_custom.dart @@ -29,7 +29,7 @@ class EditUserCustom extends State with MessageLine { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); - final itemsRole = comboRoles( + final itemsRoles = comboRoles( i18n.trDyn(GlobalTranslations.comboboxSelect), attendedPage!); attendedPage?.loadRecord( name: 'record', @@ -59,16 +59,15 @@ class EditUserCustom extends State with MessageLine { FormItem( TextFormField( controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('Email')), - validator: (input) => validateMultiple( - input, [(input) => notEmpty(input), (input) => isEmail(input)]), + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), onSaved: (value) => _fieldData.email = value ?? '', ), weight: 6), FormItem( DropdownButtonFormField( value: _fieldData.role, - items: itemsRole, + items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), onChanged: (value) => _fieldData.role = value ?? 0, @@ -90,7 +89,7 @@ class EditUserCustom extends State with MessageLine { weight: 4) ]; final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change an User')), + appBar: globalData.appBarBuilder(i18n.tr('Change User')), drawer: globalData.drawerBuilder(context), body: SafeArea( child: Form( @@ -98,11 +97,14 @@ class EditUserCustom extends State with MessageLine { child: Card( margin: EdgeInsets.symmetric( vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - ))))); + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))))); return rc; } @@ -115,27 +117,12 @@ class EditUserCustom extends State with MessageLine { final parameters = { 'module': 'Users', 'sql': 'update', - ':id': primaryKey, - ':name': _fieldData.name, - ':displayName': _fieldData.displayName, - ':email': _fieldData.email, - ':role': _fieldData.role, - ':createdBy': GlobalData.loginUserName, }; _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, '/Users/edit;$id'); - } - } + }); } @@ -145,6 +132,14 @@ class EditUserCustom extends State with MessageLine { store(); } } + + @override + void dispose() { + nameController.dispose(); + displayNameController.dispose(); + emailController.dispose(); + super.dispose(); + } } class _FieldData { @@ -152,18 +147,19 @@ class _FieldData { String displayName = ''; String email = ''; int role = 0; + void fromMap(Map map) { name = map['user_name']; displayName = map['user_displayname']; email = map['user_email']; role = map['user_role']; } - void toMap(Map map) { + map[':id'] = primaryKey; map[':name'] = name; map[':displayName'] = displayName; map[':email'] = email; map[':role'] = role; - map[':createdBy'] = GlobalData.loginUserName; + map[':changedBy'] = GlobalData.loginUserName; } } diff --git a/lib/page/users/edit_user_page.dart b/lib/page/users/edit_user_page.dart index 4b2e75f..1179663 100644 --- a/lib/page/users/edit_user_page.dart +++ b/lib/page/users/edit_user_page.dart @@ -21,7 +21,7 @@ class EditUserPage extends StatefulWidget { } class _EditUserPageState extends EditUserCustom { - _EditUserPageState(int primaryKey) : super(primaryKey); + _EditUserPageState(int primaryKey): super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/lib/page/users/list_user_custom.dart b/lib/page/users/list_user_custom.dart index 3cf2c19..f481a2c 100644 --- a/lib/page/users/list_user_custom.dart +++ b/lib/page/users/list_user_custom.dart @@ -24,20 +24,20 @@ class ListUserCustom extends State { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); - final itemsRole = comboRoles( + final itemsRoles = comboRoles( i18n.trDyn(GlobalTranslations.comboboxItemAll), attendedPage!); final formItems = [ FormItem( TextFormField( controller: textController, decoration: InputDecoration(labelText: i18n.tr('Text')), - onChanged: (value) => _fieldData.text = value, + onSaved: (value) => _fieldData.text = value ?? '', ), weight: 6), FormItem( DropdownButtonFormField( value: _fieldData.role, - items: itemsRole, + items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), onChanged: (value) => _fieldData.role = value ?? 0, @@ -47,7 +47,7 @@ class ListUserCustom extends State { ElevatedButton( onPressed: () => search(), child: Text(i18n.tr('Search'))), weight: 12, - gapAbove: padding) + gapAbove: padding), ]; final form = Form( key: _formKey, @@ -62,7 +62,6 @@ class ListUserCustom extends State { child: WidgetForm.flexibleGrid(formItems, screenWidth: attendedPage!.pageStates.screenWidth, padding: padding)))); - final rows = attendedPage!.getRows( columnList: 'user_id;user_displayname;user_email;role', what: 'query', @@ -83,10 +82,10 @@ class ListUserCustom extends State { label: Text(i18n.tr('Id')), ), DataColumn( - label: Text(i18n.tr('Name')), + label: Text(i18n.tr('Display Name')), ), DataColumn( - label: Text(i18n.tr('EMail')), + label: Text(i18n.tr('Email')), ), DataColumn( label: Text(i18n.tr('Role')), @@ -120,6 +119,11 @@ class ListUserCustom extends State { void initState() { super.initState(); } + @override + void dispose(){ + textController.dispose(); + super.dispose(); + } } class _FieldData { diff --git a/lib/page/users/list_user_page.dart b/lib/page/users/list_user_page.dart index 2723f48..1938d3d 100644 --- a/lib/page/users/list_user_page.dart +++ b/lib/page/users/list_user_page.dart @@ -20,7 +20,7 @@ class ListUserPage extends StatefulWidget { } class _ListUserPageState extends ListUserCustom { - _ListUserPageState() : super(); + _ListUserPageState(): super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; diff --git a/metatool/bin/page_generator.dart b/metatool/bin/page_generator.dart index c241876..ac6333a 100644 --- a/metatool/bin/page_generator.dart +++ b/metatool/bin/page_generator.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:dart_bones/dart_bones.dart'; +import '../lib/base/defines.dart'; import '../lib/meta/module_meta_data.dart'; import '../lib/meta/modules.dart'; import 'generator.dart'; @@ -43,7 +44,13 @@ class _EditUserPageState extends EditUserCustom { } '''; - static final templateCustom = '''// This file is created by the meta_tool. But it can be customized. + static final templateGlobalComboBox = ''' comboRolesFromBackend( + attendedPage: attendedPage!, onDone: () => setState(() => 1)); + final itemsRoles = comboRoles( + i18n.trDyn(GlobalTranslations.combobox#UNDEF), attendedPage!);'''; + + static final templateListCustom = + '''// 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'; @@ -51,39 +58,214 @@ import '../../base/i18n.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; import '../../widget/widget_form.dart'; -import 'edit_user_page.dart'; +import '../../services/global_widget.dart'; +import 'list_user_page.dart'; final i18n = I18N(); -class EditUserCustom extends State { -DEF_PRIMARY final globalData = GlobalData(); +class ListUserCustom extends State { + final globalData = GlobalData(); AttendedPage? attendedPage; + final _fieldData = _FieldData(); + final GlobalKey _formKey = + GlobalKey(debugLabel: 'CreateUser'); +#DEF_CONTROLLERS ListUserCustom(); + @override + Widget build(BuildContext context) { + final padding = GlobalThemeData.padding; +#INIT_COMBOS final formItems = [ +#FORM_ITEMS 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 rows = attendedPage!.getRows( + columnList: '#ROW_COLUMNS', + what: 'query', + parameters: { + 'module': 'Users', + 'sql': 'list', + 'offset': '0', + 'size': '10', +#PARAM_DEF }, + onDone: () => setState(() => 1), + route#EDIT1: '/Users/#EDIT2', + context: context); + final table = DataTable( + columns: [ +#TABLE_HEADER ], + rows: rows, + ); + final frameWidget = Column(children: [ + form, + SizedBox(height: padding), + SizedBox(width: double.infinity, child: table), + ]); + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Overview users')), + drawer: globalData.drawerBuilder(context), + floatingActionButton: FloatingActionButton( + onPressed: () { + globalData.navigate(context, '/Users/create'); + }, + child: const Icon(Icons.add), + //backgroundColor: Colors.green, + ), + body: SafeArea(child: frameWidget)); + return rc; + } - EditUserCustom(THIS_PRIMARY); + void search() { + //@ToDo + } + @override + void initState() { + super.initState(); + } + @override + void dispose(){ +#DISPOSE_CONTROLLER super.dispose(); + } +} + +class _FieldData { +#DEF_FIELDS} +'''; + + static final templateRecordCustom = + '''// 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/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 'edit_user_page.dart'; + +final i18n = I18N(); + +class EditUserCustom extends State with MessageLine { +#DEF_PRIMARY final globalData = GlobalData(); + AttendedPage? attendedPage; + final _fieldData = _FieldData(); + final GlobalKey _formKey = + GlobalKey(debugLabel: 'EditUser'); +#DEF_CONTROLLER EditUserCustom(#THIS_PRIMARY); @override Widget build(BuildContext context) { final padding = GlobalThemeData.padding; +#INIT_COMBO#LOAD_RECORD#ASSIGN_CONTROLLER final formItems = [ +#FORM_ITEMS FormItem( + ElevatedButton( + onPressed: () => #ACTION2(), child: Text(i18n.tr('#BUTTON'))), + weight: 8, + gapAbove: 2 * padding), + FormItem( + ElevatedButton( + onPressed: () { + attendedPage!.pageStates.dbDataState.clear(); + globalData.navigate(context, '/Users/list'); + }, + child: Text(i18n.tr('Cancel')), + ), + weight: 4) + ]; final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change User data')), + appBar: globalData.appBarBuilder(i18n.tr('#PAGE_LABEL')), drawer: globalData.drawerBuilder(context), body: SafeArea( - child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(), - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding))); + child: 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 initState() { super.initState(); } + + void #ACTION1() { + final parameters = { + 'module': 'Users', + 'sql': '#SQL_TYPE', + }; + _fieldData.toMap(parameters); + globalData.restPersistence! + .store(what: 'store', map: parameters) + .then((answer) { +#STORAGE_DONE + }); + } + + void #ACTION2() { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + #ACTION1(); + } + } + + @override + void dispose() { +#DISP_CONTROLLER super.dispose(); + } } + +class _FieldData { +#DEF_FIELD +#FROM_MAP#TO_MAP} +'''; + static final templateStorageDoneCreate = + ''' 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, '/#MODULE/edit;\$id'); + } + }'''; + static final templateStorageDoneEdit = ' setState(() => 1);'; + static final templateStorageDoneDelete = + " globalData.navigate(context, '/#MODULE/list');"; + static final templateFromMap = ''' void fromMap(Map map) { +#BODY_FROM } '''; - static final templatePageManager = '''// DO NOT CHANGE. This file is created by the meta_tool! + static final templatePageManager = + '''// DO NOT CHANGE. This file is created by the meta_tool! import 'package:flutter/material.dart'; import 'page_manager_custom.dart'; #IMPORTS# - /// Manages all meta data driven pages of the program. class PageManager { static PageManager? _instance; @@ -116,7 +298,9 @@ class PageManager { } } '''; - static final templatePageManagerCustom = '''// This file is created by the meta_tool. But it can be customized. + + static final templatePageManagerCustom = + '''// 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 'info_page.dart'; @@ -142,48 +326,320 @@ StatefulWidget? customPageByRoute(String route) { return rc; } '''; + static final templateCase = ''' case '/Users/edit': rc = EditUserPage(ARG1); break; '''; + PageGenerator(BaseLogger logger) : super(logger); - String replaceVariables(String template, PageMetaData page) { - final camelModules = toCamelCase(page.module.moduleName); - final camelModule = toCamelCase(page.module.moduleNameSingular); - final camelPageName = toCamelCase(page.name); - final rc = template - .replaceAll('Users', camelModules) - .replaceAll('User', camelModule) - .replaceAll('Edit', camelPageName) - .replaceAll('edit', page.name) - .replaceAll('users', page.module.moduleName.toLowerCase()) - .replaceAll('user', page.module.moduleNameSingular.toLowerCase()); - return rc; + String buildAssignControllers(PageMetaData page) { + final buffer = StringBuffer(); + for (var field in page.fields) { + if (field.widgetType == WidgetType.property && + (field as PropertyMetaData).displayType == DisplayType.text) { + buffer.writeln( + ' ${field.name}Controller.text = _fieldData.${field.name};'); + } + } + return buffer.toString(); + } + + String buildButton( + {required String onPressed, required String label, String indent = ''}) { + final rc = '''^ElevatedButton( +^ onPressed: () => ONPRESSED, +^ child: Text(i18n.tr('$label')))''' + .replaceAll('^', indent) + .replaceFirst('ONPRESSED', onPressed) + .replaceFirst('LABEL', label); + return rc; + } + + String buildDefinitionControllers(PageMetaData page) { + final buffer = StringBuffer(); + for (var field in page.fields) { + if (field.widgetType == WidgetType.property && + (field as PropertyMetaData).displayType == DisplayType.text) { + buffer.writeln( + ' final ${field.name}Controller = TextEditingController();'); + } + } + return buffer.toString(); + } + + String buildDefinitionFields(PageMetaData page) { + final buffer = StringBuffer(); + for (var field in page.fields) { + if (field.widgetType != WidgetType.property || + (field as PropertyMetaData).hasOption(':primary:')) { + continue; + } + final name = field.name; + final field2 = field as PropertyMetaData; + String type; + String value; + switch (field2.dataType) { + case DataType.bool: + type = 'bool'; + value = 'false'; + break; + case DataType.reference: + case DataType.int: + case DataType.nat: + type = 'int'; + value = '0'; + break; + case DataType.currency: + case DataType.float: + type = 'double'; + value = '0'; + break; + case DataType.date: + type = 'Date'; + value = 'Date()'; + break; + case DataType.datetime: + type = 'DateTime'; + value = 'DateTime()'; + break; + case DataType.string: + type = 'String'; + value = "''"; + break; + default: + type = 'Object'; + value = "''"; + break; + // throw FormatException('buildDefinitionFields(): missing ${field2.dataType}'); + } + buffer.writeln(" $type $name = $value;"); + } + return buffer.toString(); + } + + String buildDisposeControllers(PageMetaData page) { + final buffer = StringBuffer(); + for (var field in page.fields) { + if (field.widgetType == WidgetType.property && + (field as PropertyMetaData).displayType == DisplayType.text) { + buffer.writeln(' ${field.name}Controller.dispose();'); + } + } + return buffer.toString(); } + + String buildField(PropertyMetaData field, + {String indent = '', bool withController = false}) { + final name = field.name; + String rc = ''; + switch (field.displayType) { + case DisplayType.text: + final validators = []; + if (field.hasOption(':notnull:')) { + validators.add('notEmpty(input)'); + } + var validator = ''; + if (validators.length == 1) { + validator = ' validator: (input) => ${validators[0]},\n'; + } else if (validators.length > 1) { + validator = + ' validator: (input) => validateMultiple(input, [\n'; + for (var item in validators) { + validator += ' (input) => $item(input),\n'; + } + validator += ' ]),\n'; + } + rc = '''^TextFormField( +#CONTR^ decoration: InputDecoration(labelText: i18n.tr('${field.label}')), +#VALIDATOR^ onSaved: (value) => _fieldData.$name = value ?? '', +^)''' + .replaceFirst('#CONTR', + withController ? '^ controller: ${name}Controller,\n' : '') + .replaceFirst('#VALIDATOR', validator) + .replaceAll('^', indent); + break; + case DisplayType.combobox: + String name2; + if (field.dataType == DataType.reference) { + name2 = toCamelCase(field.foreignKey?.split('.')[0] ?? field.name); + } else { + name2 = field.name; + } + rc = '''^DropdownButtonFormField( +^ value: _fieldData.$name, +^ items: items$name2, +^ isExpanded: true, +^ decoration: InputDecoration(labelText: i18n.tr('${field.label}')), +^ onChanged: (value) => _fieldData.$name = value ?? 0, +^)''' + .replaceAll('^', indent); + break; + default: + throw FormatException( + 'buildField(): not implemented: ${field.displayType}'); + } + return rc; + } + + String buildFormItems(PageMetaData page, {bool withController = false}) { + final buffer = StringBuffer(); + for (var field in page.fields) { + if (field is PropertyMetaData && field.hasOption(':primary:')) { + continue; + } + final weight = (field.widgetType != WidgetType.property) + ? 6 + : (field as PropertyMetaData).weight; + buffer.writeln(' FormItem('); + if (field is PropertyMetaData) { + buffer.writeln(buildField(field, + indent: ' ', withController: withController) + + ','); + buffer.writeln(' weight: $weight),'); + } else { + throw FormatException( + 'buildFormItems(): not implemented: ${field.widgetType}'); + } + } + return buffer.toString(); + } + + String buildFromMap(PageMetaData page) { + String? rc; + if (page.pageType == PageType.edit || page.pageType == PageType.delete) { + final buffer = + StringBuffer(' void fromMap(Map map) {\n'); + for (var field in page.fields) { + if (field is PropertyMetaData && !field.hasOption(':primary:')) { + buffer.writeln(" ${field.name} = map['${field.columnName}'];"); + } + } + buffer.writeln(' }'); + rc = buffer.toString(); + } + return rc ?? ''; + } + + String buildInitializeComboBoxes(PageMetaData page) { + final buffer = StringBuffer(); + if (page.globalComboBoxes.isNotEmpty) { + for (var item in page.globalComboBoxes.split(';')) { + buffer.writeln(templateGlobalComboBox + .replaceFirst( + '#UNDEF', page.pageType == PageType.list ? 'ItemAll' : 'Select') + .replaceAll('comboRoles', item) + .replaceAll('Roles', item.substring(5))); + } + } + return buffer.toString(); + } + + String buildLoadRecord(PageMetaData page) { + String? rc; + if (page.pageType == PageType.edit || page.pageType == PageType.delete) { + rc = ''' attendedPage?.loadRecord( + name: 'record', + reload: () => setState(() => 1), + onDone: (record) => _fieldData.fromMap(record), + parameters: {'module': '${page.module.moduleName}', 'sql': 'byId', ':id': primaryKey}); +'''; + } + return rc ?? ''; + } + + String buildParamDefinitions(PageMetaData page, + {bool withController = false}) { + final buffer = StringBuffer(); + for (var field in page.fields) { + final name = field.name; + final suffix = field.widgetType != WidgetType.property || + (field as PropertyMetaData).displayType != DisplayType.text + ? '.toString()' + : ''; + buffer.writeln(" ':$name': _fieldData.$name$suffix,"); + } + return buffer.toString(); + } + + String buildTableHeader(ListPageMetaData page) { + final buffer = StringBuffer(); + for (var item in page.tableHeaders.split(';')) { + buffer.writeln(''' DataColumn( + label: Text(i18n.tr('$item')), + ),'''); + } + return buffer.toString(); + } + + String buildToMap(PageMetaData page) { + String? rc; + if (page.pageType == PageType.edit || page.pageType == PageType.create) { + final buffer = StringBuffer(' void toMap(Map map) {\n'); + if (page.pageType == PageType.edit) { + buffer.writeln(" map[':id'] = primaryKey;"); + } + for (var field in page.fields) { + if (!(field is PropertyMetaData) || field.hasOption(':primary:')) { + continue; + } + final name = field.name; + buffer.writeln(" map[':$name'] = $name;"); + } + buffer.writeln(" map[':#'] = GlobalData.loginUserName;".replaceFirst( + '#', page.pageType == PageType.edit ? 'changedBy' : 'createdBy')); + buffer.writeln(' }'); + rc = buffer.toString(); + } + return rc ?? ''; + } + /// Returns a Dart class definition of the [page] that should not be modified. String createCustomized(PageMetaData page) { - var rc = replaceVariables(templateCustom, page); - if (page.pageType == PageType.edit || page.pageType == PageType.delete) { - rc = rc.replaceAll('DEF_PRIMARY', ' final int primaryKey;\n') - .replaceAll('THIS_PRIMARY', 'this.primaryKey'); - } else { - rc = rc.replaceAll('DEF_PRIMARY', '') - .replaceAll('THIS_PRIMARY', ''); + String rc; + switch (page.pageType) { + case PageType.list: + rc = createListPage(page as ListPageMetaData); + break; + case PageType.edit: + case PageType.create: + case PageType.delete: + case PageType.custom: + rc = createRecordCustomized(page); + break; } return rc; } + /// Returns a Dart class definition of the [page] that should not be modified. + String createListPage(ListPageMetaData page) { + var rc = replaceVariables(templateListCustom, page) + .replaceFirst('#DEF_CONTROLLERS', buildDefinitionControllers(page)) + .replaceFirst('#INIT_COMBOS', buildInitializeComboBoxes(page)) + .replaceFirst('#FORM_ITEMS', buildFormItems(page, withController: true)) + .replaceFirst('#PARAM_DEF', buildParamDefinitions(page)) + .replaceFirst('#ROW_COLUMNS', page.tableColumns) + .replaceFirst('#TABLE_HEADER', buildTableHeader(page)) + .replaceFirst('#DISPOSE_CONTROLLER', buildDisposeControllers(page)) + .replaceFirst('#DEF_FIELDS', buildDefinitionFields(page)) + .replaceFirst('#EDIT1', 'Edit') + .replaceFirst('#EDIT2', 'edit'); + return rc; + } + /// Returns a Dart class definition of the [page] that should not be modified. String createPage(PageMetaData page) { var rc = replaceVariables(templatePage, page); - if (page.pageType == PageType.edit || page.pageType == PageType.delete){ - rc = rc.replaceAll('DEF_PRIMARY', ' final int primaryKey;\n') + if (page.pageType == PageType.edit || page.pageType == PageType.delete) { + rc = rc + .replaceAll('DEF_PRIMARY', ' final int primaryKey;\n') .replaceAll('THIS_PRIMARY', 'this.primaryKey') .replaceAll('PARAM_PRIMARY', 'int primaryKey') .replaceAll('VAL_PRIMARY', 'primaryKey'); } else { - rc = rc.replaceAll('DEF_PRIMARY', '') + rc = rc + .replaceAll('DEF_PRIMARY', '') .replaceAll('THIS_PRIMARY', '') .replaceAll('PARAM_PRIMARY', '') .replaceAll('VAL_PRIMARY', ''); @@ -191,19 +647,100 @@ StatefulWidget? customPageByRoute(String route) { return rc; } + /// Creates the customized part of a page displaying one record. + /// + /// [page] contains the meta data information about the page. + /// + /// Returns the file content. + String createRecordCustomized(PageMetaData page) { + var hasPrimary = false; + page.pageType == PageType.edit || page.pageType == PageType.delete; + var action1 = 'store'; + var action2 = 'verifyAndStore'; + var storageDone = ''; + var buttonText = 'Save'; + String sqlType = ''; + page.pageType == PageType.edit + ? 'update' + : (page.pageType == PageType.delete ? 'delete' : 'insert'); + + switch (page.pageType) { + case PageType.create: + sqlType = 'insert'; + storageDone = templateStorageDoneCreate.replaceFirst( + '#MODULE', page.module.moduleName); + break; + case PageType.edit: + sqlType = 'update'; + storageDone = templateStorageDoneEdit; + hasPrimary = true; + storageDone = ''; + break; + case PageType.delete: + sqlType = 'delete'; + buttonText = 'Delete'; + action1 = 'delete'; + action2 = 'verifyAndDelete'; + storageDone = templateStorageDoneDelete.replaceFirst( + '#MODULE', page.module.moduleName); + hasPrimary = true; + break; + case PageType.custom: + break; + case PageType.list: + break; + } + var rc = replaceVariables(templateRecordCustom, page) + .replaceFirst('#DEF_CONTROLLER', buildDefinitionControllers(page)) + .replaceFirst('#INIT_COMBO', buildInitializeComboBoxes(page)) + .replaceFirst('#FORM_ITEMS', buildFormItems(page, withController: true)) + .replaceFirst('#LOAD_RECORD', buildLoadRecord(page)) + .replaceFirst('#PAGE_LABEL', page.label) + .replaceFirst('#SQL_TYPE', sqlType) + .replaceAll('#ACTION1', action1) + .replaceAll('#ACTION2', action2) + .replaceFirst('#BUTTON', buttonText) + .replaceFirst('#STORAGE_DONE', storageDone) + .replaceFirst('#DEF_PARAMS', buildParamDefinitions(page)) + .replaceFirst('#DISP_CONTROLLER', buildDisposeControllers(page)) + .replaceFirst('#DEF_FIELD', buildDefinitionFields(page)) + .replaceFirst('#ASSIGN_CONTROLLER', buildAssignControllers(page)) + .replaceFirst( + '#DEF_PRIMARY', hasPrimary ? ' final int primaryKey;\n' : '') + .replaceFirst('#THIS_PRIMARY', hasPrimary ? 'this.primaryKey' : '') + .replaceFirst('#TO_MAP', buildToMap(page)) + .replaceFirst('#FROM_MAP', buildFromMap(page)); + return rc; + } + + String replaceVariables(String template, PageMetaData page) { + final camelModules = toCamelCase(page.module.moduleName); + final camelModule = toCamelCase(page.module.moduleNameSingular); + final camelPageName = toCamelCase(page.name); + final rc = template + .replaceAll('Users', camelModules) + .replaceAll('User', camelModule) + .replaceAll('Edit', camelPageName) + .replaceAll('edit', page.name) + .replaceAll('users', page.module.moduleName.toLowerCase()) + .replaceAll('user', page.module.moduleNameSingular.toLowerCase()); + return rc; + } + /// Creates the file page_manager.dart and (if it does not exist) the file /// page_manager_custom.dart. - void updatePageManager(){ + void updatePageManager() { final modules = moduleNames(); final imports = StringBuffer(); final cases = StringBuffer(); - for (var name in modules){ + for (var name in modules) { ModuleMetaData? module = moduleByName(name); for (var page in module!.pageList) { imports.writeln("import '${name.toLowerCase()}/${page.name}_" - "${module.moduleNameSingular.toLowerCase()}_page.dart';"); + "${module.moduleNameSingular.toLowerCase()}_page.dart';"); var case1 = replaceVariables(templateCase, page); - if (page.pageType == PageType.edit || page.pageType == PageType.delete){ + if (page.pageType == PageType.edit || + page.pageType == PageType.delete) { case1 = case1.replaceFirst('ARG1', 'arg1'); } else { case1 = case1.replaceFirst('ARG1', ''); @@ -211,16 +748,18 @@ StatefulWidget? customPageByRoute(String route) { cases.write(case1); } } - final content = templatePageManager.replaceFirst('#IMPORTS#', imports.toString()) - .replaceFirst('#CASES#', cases.toString()); + final content = templatePageManager + .replaceFirst('#IMPORTS#', imports.toString()) + .replaceFirst('#CASES#', cases.toString()); writeFile('../lib/page/page_manager.dart', content); final full = '../lib/page/page_manager_custom.dart'; - if (File(full).existsSync()){ + if (File(full).existsSync()) { logger.log('$full already exists. We do not override'); } else { writeFile(full, templatePageManagerCustom); } } + /// Generates all modules defined in the meta data. void updatePages(Generator generator) { if (!Directory('bin').existsSync() || @@ -234,6 +773,7 @@ StatefulWidget? customPageByRoute(String route) { if (module == null) { logger.error('+++ unknown module: $name'); } else { + module.onInitialized(); for (var page in module.pageList) { String filename = page.name + '_' +