mirror of
https://github.com/inventree/inventree-app.git
synced 2025-06-12 02:05:29 +00:00
Stock display (#379)
* Display stock quantity more prominently * Cleanup search widget * Update for stock_detail widget * More tweaks * Change bottom bar icon * Display boolean parameters appropriately * Adds ability to edit part parameters * Bump icon size a bit * Improvements to filter options - Allow filtering by "option" type - To start with, filter stock by status code * Remove debug message * Remove getTriState method - No longer needed - Remove associated unit tests * Adjust filters based on server API version * Muted colors
This commit is contained in:
@ -38,44 +38,39 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
// Override in implementing class
|
||||
String get prefix => "prefix_";
|
||||
|
||||
// Return a map of boolean filtering options available for this list
|
||||
// Should be overridden by an implementing subclass
|
||||
Map<String, Map<String, dynamic>> get filterOptions => {};
|
||||
|
||||
// Return the boolean value of a particular boolean filter
|
||||
Future<bool?> getBooleanFilterValue(String key) async {
|
||||
key = "${prefix}bool_${key}";
|
||||
Future<dynamic> getFilterValue(String key) async {
|
||||
key = "${prefix}filter_${key}";
|
||||
|
||||
Map<String, dynamic> opts = filterOptions[key] ?? {};
|
||||
dynamic backup = opts["default"];
|
||||
final result = await InvenTreeSettingsManager().getValue(key, backup);
|
||||
|
||||
bool? backup;
|
||||
dynamic v = opts["default"];
|
||||
|
||||
if (v is bool) {
|
||||
backup = v;
|
||||
}
|
||||
|
||||
final result = await InvenTreeSettingsManager().getTriState(key, backup);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Set the boolean value of a particular boolean filter
|
||||
Future<void> setBooleanFilterValue(String key, bool? value) async {
|
||||
key = "${prefix}bool_${key}";
|
||||
Future<void> setFilterValue(String key, dynamic value) async {
|
||||
key = "${prefix}filter_${key}";
|
||||
await InvenTreeSettingsManager().setValue(key, value);
|
||||
}
|
||||
|
||||
// Construct the boolean filter options for this list
|
||||
Future<Map<String, String>> constructBooleanFilters() async {
|
||||
Future<Map<String, String>> constructFilters() async {
|
||||
|
||||
Map<String, String> f = {};
|
||||
|
||||
for (String k in filterOptions.keys) {
|
||||
bool? value = await getBooleanFilterValue(k);
|
||||
dynamic value = await getFilterValue(k);
|
||||
|
||||
if (value is bool) {
|
||||
f[k] = value ? "true" : "false";
|
||||
// Skip null values
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
f[k] = value.toString();
|
||||
}
|
||||
|
||||
return f;
|
||||
@ -164,7 +159,7 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
}
|
||||
};
|
||||
|
||||
// Add in boolean filter options
|
||||
// Add in selected filter options
|
||||
for (String key in filterOptions.keys) {
|
||||
Map<String, dynamic> opts = filterOptions[key] ?? {};
|
||||
|
||||
@ -172,17 +167,18 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
String label = (opts["label"] ?? key) as String;
|
||||
String? help_text = opts["help_text"] as String?;
|
||||
|
||||
List<dynamic> choices = (opts["choices"] ?? []) as List<dynamic>;
|
||||
|
||||
bool tristate = (opts["tristate"] ?? true) as bool;
|
||||
|
||||
bool? v = await getBooleanFilterValue(key);
|
||||
dynamic v = await getFilterValue(key);
|
||||
|
||||
// Prevent null value if not tristate
|
||||
if (!tristate && v == null) {
|
||||
v = false;
|
||||
}
|
||||
|
||||
// Add in the particular field
|
||||
fields[key] = {
|
||||
Map<String, dynamic> filter = {
|
||||
"type": "boolean",
|
||||
"display_name": label,
|
||||
"label": label,
|
||||
@ -190,6 +186,16 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
"value": v,
|
||||
"tristate": (opts["tristate"] ?? true) as bool,
|
||||
};
|
||||
|
||||
if (choices.isNotEmpty) {
|
||||
// Configure as a choice input
|
||||
filter["type"] = "choice";
|
||||
filter["choices"] = choices;
|
||||
|
||||
filter.remove("tristate");
|
||||
}
|
||||
|
||||
fields[key] = filter;
|
||||
}
|
||||
|
||||
// Launch an interactive form for the user to select options
|
||||
@ -211,16 +217,7 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
|
||||
// Save boolean fields
|
||||
for (String key in filterOptions.keys) {
|
||||
|
||||
bool? v;
|
||||
|
||||
dynamic value = data[key];
|
||||
|
||||
if (value is bool) {
|
||||
v = value;
|
||||
}
|
||||
|
||||
await setBooleanFilterValue(key, v);
|
||||
await setFilterValue(key, data[key]);
|
||||
}
|
||||
|
||||
// Refresh data from the server
|
||||
@ -293,7 +290,7 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
params["ordering"] = o;
|
||||
}
|
||||
|
||||
Map<String, String> f = await constructBooleanFilters();
|
||||
Map<String, String> f = await constructFilters();
|
||||
|
||||
if (f.isNotEmpty) {
|
||||
params.addAll(f);
|
||||
@ -348,6 +345,10 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
void updateSearchTerm() {
|
||||
searchTerm = searchController.text;
|
||||
_pagingController.refresh();
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
// Function to construct a single paginated item
|
||||
@ -409,19 +410,19 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
*/
|
||||
Widget buildSearchInput(BuildContext context) {
|
||||
return ListTile(
|
||||
trailing: orderingOptions.isEmpty ? null : GestureDetector(
|
||||
child: FaIcon(FontAwesomeIcons.sort, color: COLOR_ACTION),
|
||||
leading: orderingOptions.isEmpty ? null : GestureDetector(
|
||||
child: Icon(Icons.filter_list, color: COLOR_ACTION, size: 32),
|
||||
onTap: () async {
|
||||
_saveOrderingOptions(context);
|
||||
},
|
||||
),
|
||||
leading: GestureDetector(
|
||||
trailing: GestureDetector(
|
||||
child: FaIcon(
|
||||
searchController.text.isEmpty ? FontAwesomeIcons.magnifyingGlass : FontAwesomeIcons.deleteLeft,
|
||||
color: searchController.text.isNotEmpty ? COLOR_DANGER : null,
|
||||
color: searchController.text.isNotEmpty ? COLOR_DANGER : COLOR_ACTION,
|
||||
),
|
||||
onTap: () {
|
||||
if (searchController.text.isEmpty) {
|
||||
if (searchController.text.isNotEmpty) {
|
||||
searchController.clear();
|
||||
}
|
||||
updateSearchTerm();
|
||||
|
Reference in New Issue
Block a user