diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 51576969..96eafb15 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -41,20 +41,20 @@ class _CategoryDisplayState extends RefreshableState { @override List getAppBarActions(BuildContext context) { return [ + IconButton( + icon: FaIcon(FontAwesomeIcons.search), + onPressed: () { + showSearch( + context: context, + delegate: PartSearchDelegate(filters: {"category": "${category.pk}"}) + ); + } + ), IconButton( icon: FaIcon(FontAwesomeIcons.edit), tooltip: I18N.of(context).edit, onPressed: _editCategoryDialog, ), - IconButton( - icon: FaIcon(FontAwesomeIcons.search), - onPressed: () { - showSearch( - context: context, - delegate: PartSearchDelegate(filters: {"category": "${category.pk}"}) - ); - } - ) ]; } diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 7b8ebe6a..cf45ffd3 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -6,6 +6,7 @@ import 'package:InvenTree/widget/progress.dart'; import 'package:InvenTree/widget/refreshable_state.dart'; import 'package:InvenTree/widget/fields.dart'; import 'package:InvenTree/widget/dialogs.dart'; +import 'package:InvenTree/widget/search.dart'; import 'package:InvenTree/widget/snacks.dart'; import 'package:InvenTree/widget/stock_detail.dart'; @@ -39,6 +40,15 @@ class _LocationDisplayState extends RefreshableState { @override List getAppBarActions(BuildContext context) { return [ + IconButton( + icon: FaIcon(FontAwesomeIcons.search), + onPressed: () { + showSearch( + context: context, + delegate: StockSearchDelegate(filters: {"location": "${location.pk}"}) + ); + } + ), IconButton( icon: FaIcon(FontAwesomeIcons.edit), tooltip: I18N.of(context).edit, diff --git a/lib/widget/search.dart b/lib/widget/search.dart index 7c50f8ff..1f194c1a 100644 --- a/lib/widget/search.dart +++ b/lib/widget/search.dart @@ -2,6 +2,7 @@ import 'package:InvenTree/widget/part_detail.dart'; import 'package:InvenTree/widget/progress.dart'; import 'package:InvenTree/widget/snacks.dart'; +import 'package:InvenTree/widget/stock_detail.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -12,6 +13,7 @@ import 'package:InvenTree/inventree/stock.dart'; import '../api.dart'; +// TODO - Refactor duplicate code in this file! class PartSearchDelegate extends SearchDelegate { @@ -127,12 +129,16 @@ class PartSearchDelegate extends SearchDelegate { @override Widget buildResults(BuildContext context) { - print("Build results called..."); - if (_searching) { return progressIndicator(); } + if (query.length == 0) { + return ListTile( + title: Text("Enter search query") + ); + } + if (query.length < 3) { return ListTile( title: Text("Query too short"), @@ -162,6 +168,167 @@ class PartSearchDelegate extends SearchDelegate { return Column(); } + // Ensure the search theme matches the app theme + @override + ThemeData appBarTheme(BuildContext context) { + assert(context != null); + final ThemeData theme = Theme.of(context); + assert(theme != null); + return theme; + } +} + + +class StockSearchDelegate extends SearchDelegate { + + final key = GlobalKey(); + + bool _searching = false; + + // Custom filters for the stock item search + Map filters; + + StockSearchDelegate({this.filters}) { + if (filters == null) { + filters = {}; + } + } + + // List of StockItem results + List itemResults = []; + + Future search(BuildContext context) async { + // Search string too short! + if (query.length < 3) { + itemResults.clear(); + showResults(context); + return; + } + + _searching = true; + + print("Searching..."); + + showResults(context); + + // Enable cascading part search by default + filters["cascade"] = "true"; + + final results = await InvenTreeStockItem().search( + context, query, filters: filters); + + itemResults.clear(); + + for (int idx = 0; idx < results.length; idx++) { + if (results[idx] is InvenTreeStockItem) { + itemResults.add(results[idx]); + } + } + + _searching = false; + + // TODO - Show a snackbar icon with number of results. + showSuggestions(context); + showResults(context); + } + + @override + List buildActions(BuildContext context) { + return [ + IconButton( + icon: FaIcon(FontAwesomeIcons.backspace), + onPressed: () { + query = ''; + search(context); + }, + ), + IconButton( + icon: FaIcon(FontAwesomeIcons.search), + onPressed: () { + search(context); + } + ), + ]; + } + + @override + Widget buildLeading(BuildContext context) { + return IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + this.close(context, null); + } + ); + } + + Widget _itemResult(BuildContext context, int index) { + + InvenTreeStockItem item = itemResults[index]; + + return ListTile( + title: Text(item.partName), + subtitle: Text(item.partDescription), + leading: InvenTreeAPI().getImage( + item.partThumbnail, + width: 40, + height: 40, + ), + trailing: Text(item.serialOrQuantityDisplay()), + onTap: () { + InvenTreeStockItem().get(context, item.pk).then((var it) { + if (it is InvenTreeStockItem) { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => StockDetailWidget(it)) + ); + } + }); + } + ); + } + + @override + Widget buildResults(BuildContext context) { + + if (_searching) { + return progressIndicator(); + } + + if (query.length == 0) { + return ListTile( + title: Text("Enter search query") + ); + } + + if (query.length < 3) { + return ListTile( + title: Text("Query too short"), + subtitle: Text("Enter a query of at least three characters") + ); + } + + if (itemResults.length == 0) { + return ListTile( + title: Text("No Results"), + subtitle: Text("No results matching query") + ); + } + + return ListView.separated( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + separatorBuilder: (_, __) => const Divider(height: 3), + itemBuilder: _itemResult, + itemCount: itemResults.length, + ); + } + + @override + Widget buildSuggestions(BuildContext context) { + // TODO - Implement + return Column(); + } + // Ensure the search theme matches the app theme @override ThemeData appBarTheme(BuildContext context) {