mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 13:36:50 +00:00
Display non-field errors as returned from the server
This commit is contained in:
parent
b7f9f1c55f
commit
2886f7c930
@ -7,6 +7,7 @@ import "package:date_field/date_field.dart";
|
|||||||
|
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
|
import 'package:inventree/helpers.dart';
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/inventree/sentry.dart";
|
import "package:inventree/inventree/sentry.dart";
|
||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
@ -201,7 +202,15 @@ class APIFormField {
|
|||||||
|
|
||||||
// Return the error message associated with this field
|
// Return the error message associated with this field
|
||||||
List<String> errorMessages() {
|
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 = [];
|
List<String> messages = [];
|
||||||
|
|
||||||
@ -395,7 +404,7 @@ class APIFormField {
|
|||||||
helperStyle: _helperStyle(),
|
helperStyle: _helperStyle(),
|
||||||
hintText: placeholderText,
|
hintText: placeholderText,
|
||||||
),
|
),
|
||||||
initialValue: (value ?? 0).toString(),
|
initialValue: simpleNumberString(double.tryParse(value.toString()) ?? 0),
|
||||||
keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
|
keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
|
|
||||||
@ -871,6 +880,8 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
|||||||
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
|
||||||
|
List<String> nonFieldErrors = [];
|
||||||
|
|
||||||
List<APIFormField> fields;
|
List<APIFormField> fields;
|
||||||
|
|
||||||
Function(Map<String, dynamic>)? onSuccess;
|
Function(Map<String, dynamic>)? onSuccess;
|
||||||
@ -881,6 +892,29 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
|||||||
|
|
||||||
List<Widget> widgets = [];
|
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) {
|
for (var field in fields) {
|
||||||
|
|
||||||
if (field.hidden) {
|
if (field.hidden) {
|
||||||
@ -982,7 +1016,36 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
|||||||
|
|
||||||
return response;
|
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
|
success: false
|
||||||
);
|
);
|
||||||
|
|
||||||
print(response.data);
|
|
||||||
|
|
||||||
// Update field errors
|
// Update field errors
|
||||||
for (var field in fields) {
|
for (var field in fields) {
|
||||||
field.extractErrorMessages(response);
|
field.extractErrorMessages(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extractNonFieldErrors(response);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
|
13
lib/helpers.dart
Normal file
13
lib/helpers.dart
Normal file
@ -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);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
import 'package:inventree/helpers.dart';
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:flutter/cupertino.dart";
|
import "package:flutter/cupertino.dart";
|
||||||
|
|
||||||
@ -369,16 +370,11 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
|
|
||||||
double get quantity => double.tryParse(jsondata["quantity"].toString()) ?? 0;
|
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 (includeUnits && units.isNotEmpty) {
|
||||||
if (quantity.toInt() == quantity) {
|
|
||||||
q = quantity.toInt().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (units.isNotEmpty) {
|
|
||||||
q += " ${units}";
|
q += " ${units}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,12 +390,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
return "SN ${serialNumber}";
|
return "SN ${serialNumber}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is an integer?
|
return simpleNumberString(quantity);
|
||||||
if (quantity.toInt() == quantity) {
|
|
||||||
return "${quantity.toInt()}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "${quantity}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get locationName {
|
String get locationName {
|
||||||
@ -436,7 +427,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
if (serialNumber.isNotEmpty) {
|
if (serialNumber.isNotEmpty) {
|
||||||
return "SN: $serialNumber";
|
return "SN: $serialNumber";
|
||||||
} else {
|
} else {
|
||||||
return quantityString;
|
return simpleNumberString(quantity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,7 +622,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
title: Text(L10().countStock),
|
title: Text(L10().countStock),
|
||||||
leading: FaIcon(FontAwesomeIcons.checkCircle, color: COLOR_CLICK),
|
leading: FaIcon(FontAwesomeIcons.checkCircle, color: COLOR_CLICK),
|
||||||
onTap: _countStockDialog,
|
onTap: _countStockDialog,
|
||||||
trailing: Text(item.quantityString),
|
trailing: Text(item.quantityString(includeUnits: true)),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user