From 26cdb760ebdb3e9e06e084701d0c34eaade5fe37 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 21 Jan 2021 20:32:02 +1100 Subject: [PATCH 1/3] Move the "edit" button to the app bar --- lib/widget/part_detail.dart | 17 +++++++++++++---- lib/widget/refreshable_state.dart | 5 +++++ lib/widget/stock_detail.dart | 16 ++++++++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/widget/part_detail.dart b/lib/widget/part_detail.dart index 063ec743..6e72184d 100644 --- a/lib/widget/part_detail.dart +++ b/lib/widget/part_detail.dart @@ -32,6 +32,18 @@ class _PartDisplayState extends RefreshableState { @override String getAppBarTitle(BuildContext context) { return "Part"; } + @override + List getAppBarActions(BuildContext context) { + return [ + // TODO: Hide the 'edit' button if the user does not have permission!! + IconButton( + icon: FaIcon(FontAwesomeIcons.edit), + tooltip: 'Edit', + onPressed: _editPartDialog, + ) + ]; + } + _PartDisplayState(this.part) { // TODO } @@ -114,6 +126,7 @@ class _PartDisplayState extends RefreshableState { StringField( label: "Internal Part Number", initial: part.IPN, + allowEmpty: true, onSaved: (value) => _ipn = value, ) @@ -136,10 +149,6 @@ class _PartDisplayState extends RefreshableState { title: Text("${part.fullname}"), subtitle: Text("${part.description}"), leading: InvenTreeAPI().getImage(part.thumbnail), - trailing: IconButton( - icon: FaIcon(FontAwesomeIcons.edit), - onPressed: _editPartDialog, - ), ) ) ); diff --git a/lib/widget/refreshable_state.dart b/lib/widget/refreshable_state.dart index f4a35f3b..44e48553 100644 --- a/lib/widget/refreshable_state.dart +++ b/lib/widget/refreshable_state.dart @@ -11,6 +11,10 @@ abstract class RefreshableState extends State { // Storage for context once "Build" is called BuildContext context; + List getAppBarActions(BuildContext context) { + return []; + } + String getAppBarTitle(BuildContext context) { return "App Bar Title"; } void initState() { @@ -37,6 +41,7 @@ abstract class RefreshableState extends State { AppBar getAppBar(BuildContext context) { return AppBar( title: Text(getAppBarTitle(context)), + actions: getAppBarActions(context), ); } diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 52170ae8..9e70a3c0 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -37,6 +37,18 @@ class _StockItemDisplayState extends RefreshableState { @override String getAppBarTitle(BuildContext context) { return "Stock Item"; } + @override + List getAppBarActions(BuildContext context) { + return [ + // TODO: Hide the 'edit' button if the user does not have permission!! + IconButton( + icon: FaIcon(FontAwesomeIcons.edit), + tooltip: "Edit", + onPressed: _editStockItemDialog, + ) + ]; + } + final TextEditingController _quantityController = TextEditingController(); final TextEditingController _notesController = TextEditingController(); @@ -333,10 +345,6 @@ class _StockItemDisplayState extends RefreshableState { title: Text("${item.partName}"), subtitle: Text("${item.partDescription}"), leading: InvenTreeAPI().getImage(item.partImage), - trailing: IconButton( - icon: FaIcon(FontAwesomeIcons.edit), - onPressed: _editStockItemDialog, - ) ) ) ); From 65a01ff98cf28faf114c899f431b1e21fa4b0223 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 21 Jan 2021 20:33:36 +1100 Subject: [PATCH 2/3] Rearranged stock item detail page --- lib/widget/stock_detail.dart | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 9e70a3c0..d08b89cc 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -366,6 +366,33 @@ class _StockItemDisplayState extends RefreshableState { ) ); + // Location information + if ((item.locationId > 0) && (item.locationName != null) && (item.locationName.isNotEmpty)) { + tiles.add( + ListTile( + title: Text("Stock Location"), + subtitle: Text("${item.locationPathString}"), + leading: FaIcon(FontAwesomeIcons.mapMarkerAlt), + onTap: () { + if (item.locationId > 0) { + InvenTreeStockLocation().get(context, item.locationId).then((var loc) { + Navigator.push(context, MaterialPageRoute( + builder: (context) => LocationDisplayWidget(loc))); + }); + } + }, + ) + ); + } else { + tiles.add( + ListTile( + title: Text("Stock Location"), + leading: FaIcon(FontAwesomeIcons.mapMarkerAlt), + subtitle: Text("No location set"), + ) + ); + } + // Quantity information if (item.isSerialized()) { tiles.add( @@ -399,33 +426,6 @@ class _StockItemDisplayState extends RefreshableState { ) ); - // Location information - if ((item.locationId > 0) && (item.locationName != null) && (item.locationName.isNotEmpty)) { - tiles.add( - ListTile( - title: Text("Stock Location"), - subtitle: Text("${item.locationPathString}"), - leading: FaIcon(FontAwesomeIcons.mapMarkerAlt), - onTap: () { - if (item.locationId > 0) { - InvenTreeStockLocation().get(context, item.locationId).then((var loc) { - Navigator.push(context, MaterialPageRoute( - builder: (context) => LocationDisplayWidget(loc))); - }); - } - }, - ) - ); - } else { - tiles.add( - ListTile( - title: Text("Stock Location"), - leading: FaIcon(FontAwesomeIcons.mapMarkerAlt), - subtitle: Text("No location set"), - ) - ); - } - // Supplier part? if (item.supplierPartId > 0) { tiles.add( From 3096a6d25ffcd21338cb3560c0a9d664edce9d7f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 21 Jan 2021 20:53:49 +1100 Subject: [PATCH 3/3] Stuff: - Specify image size in list view - Move "edit" button to app bar - Display part keywords - Allow editing of part keywords --- lib/api.dart | 4 +- lib/inventree/model.dart | 6 ++- lib/widget/category_display.dart | 21 +++++++-- lib/widget/location_display.dart | 22 +++++++-- lib/widget/part_detail.dart | 78 ++++++++++++++++++++------------ 5 files changed, 88 insertions(+), 43 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index f22dd1f3..f947655f 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -380,7 +380,7 @@ class InvenTreeAPI { * Load image from the InvenTree server, * or from local cache (if it has been cached!) */ - CachedNetworkImage getImage(String imageUrl) { + CachedNetworkImage getImage(String imageUrl, {double height, double width}) { if (imageUrl.isEmpty) { imageUrl = staticImage; } @@ -392,6 +392,8 @@ class InvenTreeAPI { placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(FontAwesomeIcons.exclamation), httpHeaders: defaultHeaders(), + height: height, + width: width, ); } } \ No newline at end of file diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index f9935227..0938be0c 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -47,13 +47,15 @@ class InvenTreeModel { String get description => jsondata['description'] ?? ''; - String get notes => jsondata['notes'] ?? ''; + String get notes => jsondata['notes'] as String ?? ''; - int get parentId => jsondata['parent'] ?? -1; + int get parentId => jsondata['parent'] as int ?? -1; // Legacy API provided external link as "URL", while newer API uses "link" String get link => jsondata['link'] ?? jsondata['URL'] ?? ''; + String get keywords => jsondata['keywords'] as String ?? ''; + // Create a new object from JSON data (not a constructor!) InvenTreeModel createFromJson(Map json) { diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 18f86349..73bb0d15 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -31,6 +31,17 @@ class _CategoryDisplayState extends RefreshableState { @override String getAppBarTitle(BuildContext context) { return "Part Category"; } + @override + List getAppBarActions(BuildContext context) { + return [ + IconButton( + icon: FaIcon(FontAwesomeIcons.edit), + tooltip: 'Edit', + onPressed: null, + ) + ]; + } + _CategoryDisplayState(this.category) {} // The local InvenTreePartCategory object @@ -94,10 +105,6 @@ class _CategoryDisplayState extends RefreshableState { ListTile( title: Text("${category.name}"), subtitle: Text("${category.description}"), - trailing: IconButton( - icon: FaIcon(FontAwesomeIcons.edit), - onPressed: null, - ), ), ListTile( title: Text("Parent Category"), @@ -260,7 +267,11 @@ class PartList extends StatelessWidget { return ListTile( title: Text("${part.name}"), subtitle: Text("${part.description}"), - leading: InvenTreeAPI().getImage(part.thumbnail), + leading: InvenTreeAPI().getImage( + part.thumbnail, + width: 40, + height: 40, + ), onTap: () { _openPart(context, part.pk); }, diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 359ee8f3..d61c8a70 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -28,6 +28,18 @@ class _LocationDisplayState extends RefreshableState { @override String getAppBarTitle(BuildContext context) { return "Stock Location"; } + @override + List getAppBarActions(BuildContext context) { + return [ + IconButton( + icon: FaIcon(FontAwesomeIcons.edit), + tooltip: "Edit", + // TODO - Edit stock location + onPressed: null, + ) + ]; + } + _LocationDisplayState(this.location) {} List _sublocations = List(); @@ -98,10 +110,6 @@ class _LocationDisplayState extends RefreshableState { ListTile( title: Text("${location.name}"), subtitle: Text("${location.description}"), - trailing: IconButton( - icon: FaIcon(FontAwesomeIcons.edit), - onPressed: null, - ), ), ListTile( title: Text("Parent Category"), @@ -245,7 +253,11 @@ class StockList extends StatelessWidget { return ListTile( title: Text("${item.partName}"), subtitle: Text("${item.partDescription}"), - leading: InvenTreeAPI().getImage(item.partThumbnail), + leading: InvenTreeAPI().getImage( + item.partThumbnail, + width: 40, + height: 40, + ), trailing: Text("${item.displayQuantity}", style: TextStyle(fontWeight: FontWeight.bold), ), diff --git a/lib/widget/part_detail.dart b/lib/widget/part_detail.dart index 6e72184d..ed366945 100644 --- a/lib/widget/part_detail.dart +++ b/lib/widget/part_detail.dart @@ -87,6 +87,7 @@ class _PartDisplayState extends RefreshableState { var _description; var _ipn; var _revision; + var _keywords; showFormDialog(context, "Edit Part", key: _editPartKey, @@ -107,6 +108,7 @@ class _PartDisplayState extends RefreshableState { "name": _name, "description": _description, "IPN": _ipn, + "keywords": _keywords, }); } }, @@ -128,6 +130,12 @@ class _PartDisplayState extends RefreshableState { initial: part.IPN, allowEmpty: true, onSaved: (value) => _ipn = value, + ), + StringField( + label: "Keywords", + initial: part.keywords, + allowEmpty: true, + onSaved: (value) => _keywords = value, ) ] @@ -183,37 +191,25 @@ class _PartDisplayState extends RefreshableState { ); } - // External link? - if (part.link.isNotEmpty) { - tiles.add( - ListTile( - title: Text("${part.link}"), - leading: FaIcon(FontAwesomeIcons.link), - trailing: Text(""), - onTap: null, - ) - ); - } - // Stock information tiles.add( - ListTile( - title: Text("Stock"), - leading: FaIcon(FontAwesomeIcons.boxes), - trailing: Text("${part.inStock}"), - onTap: () { - Navigator.push( + ListTile( + title: Text("Stock"), + leading: FaIcon(FontAwesomeIcons.boxes), + trailing: Text("${part.inStock}"), + onTap: () { + Navigator.push( context, MaterialPageRoute(builder: (context) => PartStockDetailWidget(part)) - ); - }, - ), + ); + }, + ), ); // Parts on order if (part.isPurchaseable) { tiles.add( - ListTile( + ListTile( title: Text("On Order"), leading: FaIcon(FontAwesomeIcons.shoppingCart), trailing: Text("${part.onOrder}"), @@ -227,11 +223,11 @@ class _PartDisplayState extends RefreshableState { if (false && part.isAssembly) { tiles.add(ListTile( - title: Text("Bill of Materials"), - leading: FaIcon(FontAwesomeIcons.thList), - trailing: Text("${part.bomItemCount}"), - onTap: null, - ) + title: Text("Bill of Materials"), + leading: FaIcon(FontAwesomeIcons.thList), + trailing: Text("${part.bomItemCount}"), + onTap: null, + ) ); tiles.add( @@ -247,9 +243,31 @@ class _PartDisplayState extends RefreshableState { // TODO - Do we want to use the app to display "used in"? if (false && part.isComponent) { tiles.add(ListTile( - title: Text("Used In"), - leading: FaIcon(FontAwesomeIcons.sitemap), - trailing: Text("${part.usedInCount}"), + title: Text("Used In"), + leading: FaIcon(FontAwesomeIcons.sitemap), + trailing: Text("${part.usedInCount}"), + onTap: null, + ) + ); + } + + // Keywords? + if (part.keywords.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${part.keywords}"), + leading: FaIcon(FontAwesomeIcons.key), + ) + ); + } + + // External link? + if (part.link.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${part.link}"), + leading: FaIcon(FontAwesomeIcons.link), + trailing: Text(""), onTap: null, ) );