mirror of
https://github.com/inventree/inventree-app.git
synced 2025-09-15 23:41:22 +00:00
List refactor (#179)
* Catch paginator bug if widget is disposed before request returns * Refactoring paginated query widget - Add option to enable / disable search filters * Major refactor of paginated search widget - Learned something new.. a state can access widget.<attribute> - THIS CHANGES EVERTHING * Preferences: Add code for tri-state values - Also improve unit testing for preferences code * Allow boolean form fields to be optionally tristate * paginator: Allow custom boolean filters * Remove outdated filtering preferences * Refactor filter options - Allow specification of more detailed options * Add custom filters for "part" list * filter tweaks * Remove legacy "SublocationList" widget * Add filtering option for locationlist * Updates for stock location widget * Refactor category display widget * More widget refactoring * Update main search widget * Fix unit tests * Improve filtering on BOM display page
This commit is contained in:
@@ -5,15 +5,20 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||
import "package:inventree/api.dart";
|
||||
import "package:inventree/app_colors.dart";
|
||||
import "package:inventree/barcode.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
|
||||
import "package:inventree/inventree/stock.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
|
||||
import "package:inventree/widget/location_list.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/stock_detail.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
import "package:inventree/widget/stock_list.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Widget for displaying detail view for a single StockLocation instance
|
||||
*/
|
||||
class LocationDisplayWidget extends StatefulWidget {
|
||||
|
||||
LocationDisplayWidget(this.location, {Key? key}) : super(key: key);
|
||||
@@ -32,6 +37,8 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
final InvenTreeStockLocation? location;
|
||||
|
||||
bool showFilterOptions = false;
|
||||
|
||||
@override
|
||||
String getAppBarTitle(BuildContext context) { return L10().stockLocation; }
|
||||
|
||||
@@ -103,19 +110,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
List<InvenTreeStockLocation> _sublocations = [];
|
||||
|
||||
String _locationFilter = "";
|
||||
|
||||
List<InvenTreeStockLocation> get sublocations {
|
||||
|
||||
if (_locationFilter.isEmpty || _sublocations.isEmpty) {
|
||||
return _sublocations;
|
||||
} else {
|
||||
return _sublocations.where((loc) => loc.filter(_locationFilter)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onBuild(BuildContext context) async {
|
||||
refresh(context);
|
||||
@@ -124,8 +118,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
|
||||
int pk = location?.pk ?? -1;
|
||||
|
||||
// Reload location information
|
||||
if (location != null) {
|
||||
final bool result = await location!.reload();
|
||||
@@ -135,17 +127,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
// Request a list of sub-locations under this one
|
||||
await InvenTreeStockLocation().list(filters: {"parent": "$pk"}).then((var locs) {
|
||||
_sublocations.clear();
|
||||
|
||||
for (var loc in locs) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
_sublocations.add(loc);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -214,7 +195,11 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
if (location == null) {
|
||||
return Card(
|
||||
child: ListTile(
|
||||
title: Text(L10().stockTopLevel),
|
||||
title: Text(
|
||||
L10().stockTopLevel,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)
|
||||
),
|
||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@@ -223,7 +208,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
ListTile(
|
||||
title: Text("${location!.name}"),
|
||||
subtitle: Text("${location!.description}"),
|
||||
trailing: Text("${location!.itemcount}"),
|
||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -286,20 +271,15 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
Widget getSelectedWidget(int index) {
|
||||
|
||||
// Construct filters for paginated stock list
|
||||
Map<String, String> filters = {};
|
||||
|
||||
if (location != null) {
|
||||
filters["location"] = "${location!.pk}";
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
return ListView(
|
||||
return Column(
|
||||
children: detailTiles(),
|
||||
);
|
||||
case 1:
|
||||
return PaginatedStockItemList(filters);
|
||||
return Column(
|
||||
children: stockTiles(),
|
||||
);
|
||||
case 2:
|
||||
return ListView(
|
||||
children: ListTile.divideTiles(
|
||||
@@ -317,8 +297,8 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
return getSelectedWidget(tabIndex);
|
||||
}
|
||||
|
||||
|
||||
List<Widget> detailTiles() {
|
||||
// Construct the "details" panel
|
||||
List<Widget> detailTiles() {
|
||||
List<Widget> tiles = [
|
||||
locationDescriptionCard(),
|
||||
ListTile(
|
||||
@@ -326,27 +306,61 @@ List<Widget> detailTiles() {
|
||||
L10().sublocations,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
trailing: sublocations.isNotEmpty ? Text("${sublocations.length}") : null,
|
||||
),
|
||||
];
|
||||
|
||||
if (loading) {
|
||||
tiles.add(progressIndicator());
|
||||
} else if (_sublocations.isNotEmpty) {
|
||||
tiles.add(SublocationList(_sublocations));
|
||||
} else {
|
||||
tiles.add(ListTile(
|
||||
title: Text(L10().sublocationNone),
|
||||
subtitle: Text(
|
||||
L10().sublocationNoneDetail,
|
||||
style: TextStyle(fontStyle: FontStyle.italic)
|
||||
trailing: GestureDetector(
|
||||
child: FaIcon(FontAwesomeIcons.filter),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
showFilterOptions = !showFilterOptions;
|
||||
});
|
||||
},
|
||||
)
|
||||
));
|
||||
}
|
||||
),
|
||||
Expanded(
|
||||
child: PaginatedStockLocationList(
|
||||
{
|
||||
"parent": location?.pk.toString() ?? "null",
|
||||
},
|
||||
showFilterOptions,
|
||||
),
|
||||
flex: 10,
|
||||
)
|
||||
];
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
// Construct the "stock" panel
|
||||
List<Widget> stockTiles() {
|
||||
|
||||
Map<String, String> filters = {
|
||||
"location": location?.pk.toString() ?? "null",
|
||||
};
|
||||
|
||||
return [
|
||||
locationDescriptionCard(includeActions: false),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10().stock,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
trailing: GestureDetector(
|
||||
child: FaIcon(FontAwesomeIcons.filter),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
showFilterOptions = !showFilterOptions;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: PaginatedStockItemList(
|
||||
filters,
|
||||
showFilterOptions,
|
||||
),
|
||||
flex: 10,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> actionTiles() {
|
||||
List<Widget> tiles = [];
|
||||
@@ -452,48 +466,4 @@ List<Widget> detailTiles() {
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SublocationList extends StatelessWidget {
|
||||
|
||||
const SublocationList(this._locations);
|
||||
|
||||
final List<InvenTreeStockLocation> _locations;
|
||||
|
||||
void _openLocation(BuildContext context, int pk) {
|
||||
|
||||
InvenTreeStockLocation().get(pk).then((var loc) {
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _build(BuildContext context, int index) {
|
||||
InvenTreeStockLocation loc = _locations[index];
|
||||
|
||||
return ListTile(
|
||||
title: Text("${loc.name}"),
|
||||
subtitle: Text("${loc.description}"),
|
||||
trailing: Text("${loc.itemcount}"),
|
||||
onTap: () {
|
||||
_openLocation(context, loc.pk);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: ClampingScrollPhysics(),
|
||||
itemBuilder: _build,
|
||||
separatorBuilder: (_, __) => const Divider(height: 3),
|
||||
itemCount: _locations.length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user