diff --git a/lib/api_form.dart b/lib/api_form.dart index 4204213b..c83b945a 100644 --- a/lib/api_form.dart +++ b/lib/api_form.dart @@ -7,6 +7,7 @@ import "package:date_field/date_field.dart"; import "package:inventree/api.dart"; import "package:inventree/app_colors.dart"; +import 'package:inventree/helpers.dart'; import "package:inventree/inventree/part.dart"; import "package:inventree/inventree/sentry.dart"; import "package:inventree/inventree/stock.dart"; @@ -201,7 +202,15 @@ class APIFormField { // Return the error message associated with this field List<String> errorMessages() { - List<dynamic> errors = (data["errors"] ?? []) as List<dynamic>; + + dynamic errors = data["errors"] ?? []; + + // Handle the case where a single error message is returned + if (errors is String) { + errors = [errors]; + } + + errors = errors as List<dynamic>; List<String> messages = []; @@ -395,7 +404,7 @@ class APIFormField { helperStyle: _helperStyle(), hintText: placeholderText, ), - initialValue: (value ?? 0).toString(), + initialValue: simpleNumberString(double.tryParse(value.toString()) ?? 0), keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true), validator: (value) { @@ -871,6 +880,8 @@ class _APIFormWidgetState extends State<APIFormWidget> { final IconData icon; + List<String> nonFieldErrors = []; + List<APIFormField> fields; Function(Map<String, dynamic>)? onSuccess; @@ -881,6 +892,29 @@ class _APIFormWidgetState extends State<APIFormWidget> { List<Widget> widgets = []; + // Display non-field errors first + if (nonFieldErrors.isNotEmpty) { + for (String error in nonFieldErrors) { + widgets.add( + ListTile( + title: Text( + error, + style: TextStyle( + color: COLOR_DANGER, + ), + ), + leading: FaIcon( + FontAwesomeIcons.exclamationCircle, + color: COLOR_DANGER + ), + ) + ); + } + + widgets.add(Divider(height: 5)); + + } + for (var field in fields) { if (field.hidden) { @@ -982,7 +1016,36 @@ class _APIFormWidgetState extends State<APIFormWidget> { return response; } + } + void extractNonFieldErrors(APIResponse response) { + + List<String> errors = []; + + Map<String, dynamic> data = response.asMap(); + + // Potential keys representing non-field errors + List<String> keys = [ + "__all__", + "non_field_errors", + "errors", + ]; + + for (String key in keys) { + if (data.containsKey(key)) { + dynamic result = data[key]; + + if (result is String) { + errors.add(result); + } else if (result is List) { + result.forEach((element) { + errors.add(element.toString()); + }); + } + } + } + + nonFieldErrors = errors; } /* @@ -1073,13 +1136,13 @@ class _APIFormWidgetState extends State<APIFormWidget> { success: false ); - print(response.data); - // Update field errors for (var field in fields) { field.extractErrorMessages(response); } + extractNonFieldErrors(response); + break; case 401: showSnackIcon( diff --git a/lib/helpers.dart b/lib/helpers.dart new file mode 100644 index 00000000..942c98ec --- /dev/null +++ b/lib/helpers.dart @@ -0,0 +1,13 @@ +/* + * A set of helper functions to reduce boilerplate code + */ + +/* + * Simplify a numerical value into a string, + * supressing trailing zeroes + */ +String simpleNumberString(double number) { + // Ref: https://stackoverflow.com/questions/55152175/how-to-remove-trailing-zeros-using-dart + + return number.toStringAsFixed(number.truncateToDouble() == number ? 0 : 1); +} \ No newline at end of file diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 82c37711..1aa709a7 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -1,6 +1,7 @@ import "dart:async"; import "package:intl/intl.dart"; +import 'package:inventree/helpers.dart'; import "package:inventree/inventree/part.dart"; import "package:flutter/cupertino.dart"; @@ -369,16 +370,11 @@ class InvenTreeStockItem extends InvenTreeModel { double get quantity => double.tryParse(jsondata["quantity"].toString()) ?? 0; - String get quantityString { + String quantityString({bool includeUnits = false}){ - String q = quantity.toString(); + String q = simpleNumberString(quantity); - // Simplify integer values e.g. "1.0" becomes "1" - if (quantity.toInt() == quantity) { - q = quantity.toInt().toString(); - } - - if (units.isNotEmpty) { + if (includeUnits && units.isNotEmpty) { q += " ${units}"; } @@ -394,12 +390,7 @@ class InvenTreeStockItem extends InvenTreeModel { return "SN ${serialNumber}"; } - // Is an integer? - if (quantity.toInt() == quantity) { - return "${quantity.toInt()}"; - } - - return "${quantity}"; + return simpleNumberString(quantity); } String get locationName { @@ -436,7 +427,7 @@ class InvenTreeStockItem extends InvenTreeModel { if (serialNumber.isNotEmpty) { return "SN: $serialNumber"; } else { - return quantityString; + return simpleNumberString(quantity); } } diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index c10ac170..2fa41ddb 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -622,7 +622,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> { title: Text(L10().countStock), leading: FaIcon(FontAwesomeIcons.checkCircle, color: COLOR_CLICK), onTap: _countStockDialog, - trailing: Text(item.quantityString), + trailing: Text(item.quantityString(includeUnits: true)), ) );