Settings
This commit is contained in:
parent
10fa911cef
commit
a73adb462b
|
|
@ -124,6 +124,12 @@ const String kEntityStateActive = 'active';
|
|||
const String kEntityStateArchived = 'archived';
|
||||
const String kEntityStateDeleted = 'deleted';
|
||||
|
||||
const String kFieldTypeSingleLineText = 'single_line_text';
|
||||
const String kFieldTypeMultiLineText = 'multi_line_text';
|
||||
const String kFieldTypeDropdown = 'dropdown';
|
||||
const String kFieldTypeDate = 'date';
|
||||
const String kFieldTypeSwitch = 'switch';
|
||||
|
||||
const String kTaskStatusLogged = '-1';
|
||||
const String kTaskStatusRunning = '-2';
|
||||
const String kTaskStatusInvoiced = '-3';
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
|
|||
@override
|
||||
String toString() {
|
||||
//return 'Custom fields [UI]: ${uiState.settingsUIState.userCompany.company.customFields}, [DB] ${selectedCompany.customFields}';
|
||||
return 'template: ${uiState.settingsUIState.settings.emailBodyInvoice}';
|
||||
return 'custom fields: ${uiState.settingsUIState.userCompany.company.customFields}';
|
||||
//return 'defaultInvoiceDesignId: ${selectedCompany.settings.defaultInvoiceDesignId}';
|
||||
//return 'Routes: Current: ${uiState.currentRoute} Prev: ${uiState.previousRoute}';
|
||||
//return 'Route: ${uiState.currentRoute}, Setting Type: ${uiState.settingsUIState.entityType}, Name: ${uiState.settingsUIState.settings.name}, Updated: ${uiState.settingsUIState.updatedAt}';
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ class DecoratedFormField extends StatelessWidget {
|
|||
this.textInputAction,
|
||||
this.onFieldSubmitted,
|
||||
this.enabled,
|
||||
this.hint,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final String label;
|
||||
final String hint;
|
||||
final Function(String) validator;
|
||||
final TextInputType keyboardType;
|
||||
final int maxLines;
|
||||
|
|
@ -31,6 +33,7 @@ class DecoratedFormField extends StatelessWidget {
|
|||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
hintText: hint,
|
||||
),
|
||||
validator: validator,
|
||||
keyboardType: null,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/app_dropdown_button.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/custom_fields_vm.dart';
|
||||
|
|
@ -116,7 +118,7 @@ class _CustomFieldsState extends State<CustomFields>
|
|||
}
|
||||
}
|
||||
|
||||
class CustomFieldsSettings extends StatefulWidget {
|
||||
class CustomFieldsSettings extends StatelessWidget {
|
||||
const CustomFieldsSettings({
|
||||
@required this.fieldType,
|
||||
@required this.viewModel,
|
||||
|
|
@ -128,14 +130,82 @@ class CustomFieldsSettings extends StatefulWidget {
|
|||
final String fieldType;
|
||||
|
||||
@override
|
||||
_CustomFieldsSettingsState createState() => _CustomFieldsSettingsState();
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final company = viewModel.company;
|
||||
|
||||
return FormCard(
|
||||
children: <Widget>[
|
||||
CustomFormField(
|
||||
label: localization.lookup('${fieldType}_field'),
|
||||
value: company.customFields['${fieldType}1'],
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..customFields['${fieldType}1'] = value)),
|
||||
showTaxes: showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes1,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes1 = value)),
|
||||
),
|
||||
CustomFormField(
|
||||
label: localization.lookup('${fieldType}_field'),
|
||||
value: company.customFields['${fieldType}2'],
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..customFields['${fieldType}1'] = value)),
|
||||
showTaxes: showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes2,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes2 = value)),
|
||||
),
|
||||
CustomFormField(
|
||||
label: localization.lookup('${fieldType}_field'),
|
||||
value: company.customFields['${fieldType}3'],
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..customFields['${fieldType}1'] = value)),
|
||||
showTaxes: showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes3,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes3 = value)),
|
||||
),
|
||||
CustomFormField(
|
||||
label: localization.lookup('${fieldType}_field'),
|
||||
value: company.customFields['${fieldType}4'],
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..customFields['${fieldType}1'] = value)),
|
||||
showTaxes: showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes4,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes4 = value)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CustomFieldsSettingsState extends State<CustomFieldsSettings> {
|
||||
final _customField1Controller = TextEditingController();
|
||||
final _customField2Controller = TextEditingController();
|
||||
final _customField3Controller = TextEditingController();
|
||||
final _customField4Controller = TextEditingController();
|
||||
class CustomFormField extends StatefulWidget {
|
||||
const CustomFormField({
|
||||
@required this.label,
|
||||
@required this.onChanged,
|
||||
@required this.value,
|
||||
this.showTaxes = false,
|
||||
this.taxesEnabled,
|
||||
this.onTaxesChanged,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String value;
|
||||
final bool showTaxes;
|
||||
final bool taxesEnabled;
|
||||
final Function(String) onChanged;
|
||||
final Function(bool) onTaxesChanged;
|
||||
|
||||
@override
|
||||
_CustomFormFieldState createState() => _CustomFormFieldState();
|
||||
}
|
||||
|
||||
class _CustomFormFieldState extends State<CustomFormField> {
|
||||
final _customFieldController = TextEditingController();
|
||||
final _optionsController = TextEditingController();
|
||||
String _fieldType = kFieldTypeMultiLineText;
|
||||
|
||||
List<TextEditingController> _controllers = [];
|
||||
final _debouncer = Debouncer();
|
||||
|
|
@ -152,21 +222,34 @@ class _CustomFieldsSettingsState extends State<CustomFieldsSettings> {
|
|||
@override
|
||||
void didChangeDependencies() {
|
||||
_controllers = [
|
||||
_customField1Controller,
|
||||
_customField2Controller,
|
||||
_customField3Controller,
|
||||
_customField4Controller,
|
||||
_customFieldController,
|
||||
_optionsController,
|
||||
];
|
||||
|
||||
_controllers
|
||||
.forEach((dynamic controller) => controller.removeListener(_onChanged));
|
||||
|
||||
final fieldType = widget.fieldType;
|
||||
final customFields = widget.viewModel.company.customFields;
|
||||
_customField1Controller.text = customFields['${fieldType}1'];
|
||||
_customField2Controller.text = customFields['${fieldType}2'];
|
||||
_customField3Controller.text = customFields['${fieldType}3'];
|
||||
_customField4Controller.text = customFields['${fieldType}4'];
|
||||
if ('${widget.value ?? ''}'.isNotEmpty && widget.value.contains('|')) {
|
||||
final parts = widget.value.split('|');
|
||||
_customFieldController.text = parts[0];
|
||||
switch (parts[1]) {
|
||||
case kFieldTypeSingleLineText:
|
||||
_fieldType = kFieldTypeSingleLineText;
|
||||
break;
|
||||
case kFieldTypeDate:
|
||||
_fieldType = kFieldTypeDate;
|
||||
break;
|
||||
case kFieldTypeSwitch:
|
||||
_fieldType = kFieldTypeSwitch;
|
||||
break;
|
||||
default:
|
||||
_fieldType = kFieldTypeDropdown;
|
||||
_optionsController.text = parts[1];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_customFieldController.text = widget.value;
|
||||
}
|
||||
|
||||
_controllers
|
||||
.forEach((dynamic controller) => controller.addListener(_onChanged));
|
||||
|
|
@ -176,107 +259,95 @@ class _CustomFieldsSettingsState extends State<CustomFieldsSettings> {
|
|||
|
||||
void _onChanged() {
|
||||
_debouncer.run(() {
|
||||
final viewModel = widget.viewModel;
|
||||
final fieldType = widget.fieldType;
|
||||
final company = widget.viewModel.company;
|
||||
final origFields = company.customFields;
|
||||
|
||||
final updatedFields = origFields.rebuild((b) => b
|
||||
..addAll({'${fieldType}1': _customField1Controller.text.trim()})
|
||||
..addAll({'${fieldType}2': _customField2Controller.text.trim()})
|
||||
..addAll({'${fieldType}3': _customField3Controller.text.trim()})
|
||||
..addAll({'${fieldType}4': _customField4Controller.text.trim()}));
|
||||
|
||||
if (viewModel.company.customFields != updatedFields) {
|
||||
viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..customFields.replace(updatedFields)));
|
||||
var value = _customFieldController.text.trim();
|
||||
if ([
|
||||
kFieldTypeSingleLineText,
|
||||
kFieldTypeDate,
|
||||
kFieldTypeSwitch,
|
||||
].contains(_fieldType)) {
|
||||
value = '$value|$_fieldType';
|
||||
} else if (_fieldType == kFieldTypeDropdown) {
|
||||
value = '$value|${_optionsController.text.trim()}';
|
||||
}
|
||||
widget.onChanged(value);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final viewModel = widget.viewModel;
|
||||
final company = viewModel.company;
|
||||
|
||||
return FormCard(
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
CustomFormField(
|
||||
label: localization.lookup('${widget.fieldType}_field'),
|
||||
controller: _customField1Controller,
|
||||
showTaxes: widget.showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes1,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes1 = value)),
|
||||
),
|
||||
CustomFormField(
|
||||
label: localization.lookup('${widget.fieldType}_field'),
|
||||
controller: _customField2Controller,
|
||||
showTaxes: widget.showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes2,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes2 = value)),
|
||||
),
|
||||
CustomFormField(
|
||||
label: localization.lookup('${widget.fieldType}_field'),
|
||||
controller: _customField3Controller,
|
||||
showTaxes: widget.showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes3,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes3 = value)),
|
||||
),
|
||||
CustomFormField(
|
||||
label: localization.lookup('${widget.fieldType}_field'),
|
||||
controller: _customField4Controller,
|
||||
showTaxes: widget.showChargeTaxes,
|
||||
taxesEnabled: company.enableCustomSurchargeTaxes4,
|
||||
onTaxesChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..enableCustomSurchargeTaxes4 = value)),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: DecoratedFormField(
|
||||
label: widget.label,
|
||||
controller: _customFieldController,
|
||||
),
|
||||
),
|
||||
if (widget.showTaxes) ...[
|
||||
Checkbox(
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
value: widget.taxesEnabled ?? false,
|
||||
onChanged: widget.onTaxesChanged,
|
||||
),
|
||||
GestureDetector(
|
||||
child: Text(localization.chargeTaxes),
|
||||
onTap: () => widget.onTaxesChanged(!widget.taxesEnabled),
|
||||
),
|
||||
] else ...[
|
||||
SizedBox(width: 20),
|
||||
Flexible(
|
||||
child: AppDropdownButton(
|
||||
labelText: localization.fieldType,
|
||||
value: _fieldType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_fieldType = value;
|
||||
_onChanged();
|
||||
});
|
||||
},
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
child: Text(localization.singleLineText),
|
||||
value: kFieldTypeSingleLineText,
|
||||
),
|
||||
DropdownMenuItem(
|
||||
child: Text(localization.multiLineText),
|
||||
value: kFieldTypeMultiLineText,
|
||||
),
|
||||
DropdownMenuItem(
|
||||
child: Text(localization.switchLabel),
|
||||
value: kFieldTypeSwitch,
|
||||
),
|
||||
DropdownMenuItem(
|
||||
child: Text(localization.dropdown),
|
||||
value: kFieldTypeDropdown,
|
||||
),
|
||||
DropdownMenuItem(
|
||||
child: Text(localization.date),
|
||||
value: kFieldTypeDate,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomFormField extends StatelessWidget {
|
||||
const CustomFormField({
|
||||
@required this.label,
|
||||
@required this.controller,
|
||||
this.showTaxes = false,
|
||||
this.taxesEnabled,
|
||||
this.onTaxesChanged,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final TextEditingController controller;
|
||||
final bool showTaxes;
|
||||
final bool taxesEnabled;
|
||||
final Function(bool) onTaxesChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: DecoratedFormField(
|
||||
label: label,
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
if (showTaxes) ...[
|
||||
Checkbox(
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
value: taxesEnabled ?? false,
|
||||
onChanged: onTaxesChanged,
|
||||
),
|
||||
GestureDetector(
|
||||
child: Text(localization.chargeTaxes),
|
||||
onTap: () => onTaxesChanged(!taxesEnabled),
|
||||
),
|
||||
]
|
||||
if (_fieldType == kFieldTypeDropdown)
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: DecoratedFormField(
|
||||
label: localization.options,
|
||||
controller: _optionsController,
|
||||
hint: localization.useCommaSeparatedList,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ abstract class LocaleCodeAware {
|
|||
mixin LocalizationsProvider on LocaleCodeAware {
|
||||
static final Map<String, Map<String, String>> _localizedValues = {
|
||||
'en': {
|
||||
'switch': 'Switch',
|
||||
'use_comma_sparated_list': 'Use a comma separated list',
|
||||
'options': 'Options',
|
||||
'single_line_text': 'Single-line text',
|
||||
'multi_line_text': 'Multi-line text',
|
||||
'dropdown': 'Dropdown',
|
||||
'field_type': 'Field Type',
|
||||
'recover_password_email_sent': 'A password recovery email has been sent',
|
||||
'submit': 'Submit',
|
||||
'recover_password': 'Recover Password',
|
||||
|
|
@ -15461,6 +15468,21 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
String get recoverPasswordEmailSent =>
|
||||
_localizedValues[localeCode]['recover_password_email_sent'];
|
||||
|
||||
String get fieldType => _localizedValues[localeCode]['field_type'];
|
||||
|
||||
String get singleLineText => _localizedValues[localeCode]['single_line_text'];
|
||||
|
||||
String get multiLineText => _localizedValues[localeCode]['multi_line_text'];
|
||||
|
||||
String get dropdown => _localizedValues[localeCode]['dropdown'];
|
||||
|
||||
String get options => _localizedValues[localeCode]['options'];
|
||||
|
||||
String get useCommaSeparatedList =>
|
||||
_localizedValues[localeCode]['use_comma_sparated_list'];
|
||||
|
||||
String get switchLabel => _localizedValues[localeCode]['switch'];
|
||||
|
||||
String lookup(String key) {
|
||||
final lookupKey = toSnakeCase(key);
|
||||
return _localizedValues[localeCode][lookupKey] ??
|
||||
|
|
|
|||
Loading…
Reference in New Issue