diff --git a/lib/api.dart b/lib/api.dart index 99eccb82..843cfeb4 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -599,8 +599,6 @@ class InvenTreeAPI { Uri? _uri = Uri.tryParse(_url); - print("apiRequest ${method} -> ${url}"); - if (_uri == null) { showServerError(L10().invalidHost, L10().invalidHostDetails); return null; @@ -627,12 +625,15 @@ class InvenTreeAPI { return _request; } on SocketException catch (error) { + print("SocketException at ${url}: ${error.toString()}"); showServerError(L10().connectionRefused, error.toString()); return null; } on TimeoutException { + print("TimeoutException at ${url}"); showTimeoutError(); return null; } catch (error, stackTrace) { + print("Server error at ${url}: ${error.toString()}"); showServerError(L10().serverError, error.toString()); sentryReportError(error, stackTrace); return null; diff --git a/lib/api_form.dart b/lib/api_form.dart index f9705f9f..6b59f927 100644 --- a/lib/api_form.dart +++ b/lib/api_form.dart @@ -111,6 +111,8 @@ class APIFormField { String get placeholderText => (data['placeholder'] ?? '').toString(); + List get choices => data["choices"] ?? []; + Future loadInitialData() async { // Only for "related fields" @@ -151,6 +153,8 @@ class APIFormField { return _constructBoolean(); case "related field": return _constructRelatedField(); + case "choice": + return _constructChoiceField(); default: return ListTile( title: Text( @@ -163,6 +167,40 @@ class APIFormField { } } + Widget _constructChoiceField() { + + dynamic _initial; + + // Check if the current value is within the allowed values + for (var opt in choices) { + if (opt['value'] == value) { + _initial = opt; + break; + } + } + + return DropdownSearch( + mode: Mode.BOTTOM_SHEET, + showSelectedItem: false, + selectedItem: _initial, + items: choices, + label: label, + hint: helpText, + onChanged: null, + showClearButton: !required, + itemAsString: (dynamic item) { + return item['display_name']; + }, + onSaved: (item) { + if (item == null) { + data['value'] = null; + } else { + data['value'] = item['value']; + } + } + ); + } + // Construct an input for a related field Widget _constructRelatedField() { diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index 65e3f2e6..f7e53adb 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -276,8 +276,6 @@ class InvenTreeModel { params[key] = filters[key] ?? ''; } - print("LIST: $URL ${params.toString()}"); - var response = await api.get(URL, params: params); // A list of "InvenTreeModel" items diff --git a/lib/l10n b/lib/l10n index 94e1193e..4dccf7c3 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 94e1193ef17ad536afc92b3b071cb3335e7d828c +Subproject commit 4dccf7c37fe5bca94fe0ef9ab05f7d5f0ec52452 diff --git a/lib/settings/login.dart b/lib/settings/login.dart index e6a8dd1e..5112161a 100644 --- a/lib/settings/login.dart +++ b/lib/settings/login.dart @@ -56,7 +56,7 @@ class _InvenTreeLoginSettingsState extends State { key: _addProfileKey, callback: () { if (createNew) { - // TODO - create the new profile... + UserProfile profile = UserProfile( name: _name, server: _server, diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 1029b021..009c94cc 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -20,6 +20,8 @@ import 'package:inventree/api.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import '../api_form.dart'; + class StockDetailWidget extends StatefulWidget { StockDetailWidget(this.item, {Key? key}) : super(key: key); @@ -49,20 +51,29 @@ class _StockItemDisplayState extends RefreshableState { @override List getAppBarActions(BuildContext context) { - return [ - IconButton( - icon: FaIcon(FontAwesomeIcons.globe), - onPressed: _openInvenTreePage, - ), - // TODO: Hide the 'edit' button if the user does not have permission!! - /* - IconButton( - icon: FaIcon(FontAwesomeIcons.edit), - tooltip: L10().edit, - onPressed: _editPartDialog, - ) - */ - ]; + + List actions = []; + + if (InvenTreeAPI().checkPermission('stock', 'view')) { + actions.add( + IconButton( + icon: FaIcon(FontAwesomeIcons.globe), + onPressed: _openInvenTreePage, + ) + ); + } + + if (InvenTreeAPI().checkPermission('stock', 'change')) { + actions.add( + IconButton( + icon: FaIcon(FontAwesomeIcons.edit), + tooltip: L10().edit, + onPressed: () { _editStockItem(context); }, + ) + ); + } + + return actions; } Future _openInvenTreePage() async { @@ -95,6 +106,24 @@ class _StockItemDisplayState extends RefreshableState { await item.getTestResults(); } + void _editStockItem(BuildContext context) async { + + launchApiForm( + context, + L10().editItem, + item.url, + { + "status": {}, + "batch": {}, + "packaging": {}, + "link": {}, + }, + modelData: item.jsondata, + onSuccess: refresh + ); + + } + void _addStock() async { double quantity = double.parse(_quantityController.text);