2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-06-16 04:05:28 +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:
Oliver
2022-07-19 23:29:01 +10:00
committed by GitHub
parent e03a8561b9
commit 13ebaf43e1
23 changed files with 667 additions and 403 deletions

View File

@ -1,14 +1,15 @@
import "package:flutter/material.dart";
import "package:font_awesome_flutter/font_awesome_flutter.dart";
import "package:inventree/api.dart";
import "package:inventree/app_colors.dart";
import "package:inventree/inventree/part.dart";
import "package:inventree/widget/part_list.dart";
import "package:inventree/widget/progress.dart";
import "package:inventree/widget/snacks.dart";
import "package:inventree/l10.dart";
import "package:inventree/inventree/part.dart";
import "package:inventree/widget/category_list.dart";
import "package:inventree/widget/part_list.dart";
import "package:inventree/widget/snacks.dart";
import "package:inventree/widget/part_detail.dart";
import "package:inventree/widget/refreshable_state.dart";
@ -28,6 +29,8 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
_CategoryDisplayState(this.category);
bool showFilterOptions = false;
@override
String getAppBarTitle(BuildContext context) => L10().partCategory;
@ -73,8 +76,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
// The local InvenTreePartCategory object
final InvenTreePartCategory? category;
List<InvenTreePartCategory> _subcategories = [];
@override
Future<void> onBuild(BuildContext context) async {
refresh(context);
@ -83,8 +84,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
@override
Future<void> request(BuildContext context) async {
int pk = category?.pk ?? -1;
// Update the category
if (category != null) {
final bool result = await category?.reload() ?? false;
@ -93,27 +92,17 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
Navigator.of(context).pop();
}
}
// Request a list of sub-categories under this one
await InvenTreePartCategory().list(filters: {"parent": "$pk"}).then((var cats) {
_subcategories.clear();
for (var cat in cats) {
if (cat is InvenTreePartCategory) {
_subcategories.add(cat);
}
}
// Update state
setState(() {});
});
}
Widget getCategoryDescriptionCard({bool extra = true}) {
if (category == null) {
return Card(
child: ListTile(
title: Text(L10().partCategoryTopLevel)
leading: FaIcon(FontAwesomeIcons.shapes),
title: Text(
L10().partCategoryTopLevel,
style: TextStyle(fontStyle: FontStyle.italic),
)
)
);
} else {
@ -182,7 +171,9 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
);
}
// Construct the "details" panel
List<Widget> detailTiles() {
List<Widget> tiles = <Widget>[
getCategoryDescriptionCard(),
ListTile(
@ -190,27 +181,62 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
L10().subcategories,
style: TextStyle(fontWeight: FontWeight.bold)
),
trailing: _subcategories.isNotEmpty ? Text("${_subcategories.length}") : null,
trailing: GestureDetector(
child: FaIcon(FontAwesomeIcons.filter),
onTap: () async {
setState(() {
showFilterOptions = !showFilterOptions;
});
},
)
),
Expanded(
child: PaginatedPartCategoryList(
{
"parent": category?.pk.toString() ?? "null"
},
showFilterOptions,
),
flex: 10,
)
];
if (loading) {
tiles.add(progressIndicator());
} else if (_subcategories.isEmpty) {
tiles.add(ListTile(
title: Text(L10().noSubcategories),
subtitle: Text(
L10().noSubcategoriesAvailable,
style: TextStyle(fontStyle: FontStyle.italic)
)
));
} else {
tiles.add(SubcategoryList(_subcategories));
}
return tiles;
}
// Construct the "parts" panel
List<Widget> partsTiles() {
Map<String, String> filters = {
"category": category?.pk.toString() ?? "null",
};
return [
getCategoryDescriptionCard(extra: false),
ListTile(
title: Text(
L10().parts,
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: GestureDetector(
child: FaIcon(FontAwesomeIcons.filter),
onTap: () async {
setState(() {
showFilterOptions = !showFilterOptions;
});
},
),
),
Expanded(
child: PaginatedPartList(
filters,
showFilterOptions,
),
flex: 10,
)
];
}
Future<void> _newCategory(BuildContext context) async {
int pk = category?.pk ?? -1;
@ -323,14 +349,12 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
switch (tabIndex) {
case 0:
return ListView(
children: detailTiles()
return Column(
children: detailTiles()
);
case 1:
return PaginatedPartList(
{
"category": "${category?.pk ?? 'null'}"
},
return Column(
children: partsTiles()
);
case 2:
return ListView(
@ -341,47 +365,3 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
}
}
}
/*
* Builder for displaying a list of PartCategory objects
*/
class SubcategoryList extends StatelessWidget {
const SubcategoryList(this._categories);
final List<InvenTreePartCategory> _categories;
void _openCategory(BuildContext context, int pk) {
// Attempt to load the sub-category.
InvenTreePartCategory().get(pk).then((var cat) {
if (cat is InvenTreePartCategory) {
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
}
});
}
Widget _build(BuildContext context, int index) {
InvenTreePartCategory cat = _categories[index];
return ListTile(
title: Text("${cat.name}"),
subtitle: Text("${cat.description}"),
trailing: Text("${cat.partcount}"),
onTap: () {
_openCategory(context, cat.pk);
}
);
}
@override
Widget build(BuildContext context) {
return ListView.separated(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
separatorBuilder: (_, __) => const Divider(height: 3),
itemBuilder: _build, itemCount: _categories.length);
}
}