2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-29 05:56:47 +00:00

Merge pull request #19 from SchrodingersGat/stock-item-page

Improve view for StockItem
This commit is contained in:
Oliver 2020-04-06 22:52:24 +10:00 committed by GitHub
commit c7fbe99f53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 459 additions and 130 deletions

View File

@ -72,8 +72,46 @@ class InvenTreeAPI {
// Authentication token (initially empty, must be requested) // Authentication token (initially empty, must be requested)
String _token = ""; String _token = "";
bool isConnected() {
return _token.isNotEmpty;
}
/*
* Check server connection and display messages if not connected.
* Useful as a precursor check before performing operations.
*/
bool checkConnection(BuildContext context) {
// Firstly, is the server connected?
if (!isConnected()) {
showDialog(
context: context,
child: new SimpleDialog(
title: new Text("Not Connected"),
children: <Widget> [
ListTile(
title: Text("Server not connected"),
)
]
)
);
return false;
}
// Is the server version too old?
// TODO
// Finally
return true;
}
// Server instance information
String instance = '';
// Server version information // Server version information
String _version; String _version = '';
// Getter for server version information // Getter for server version information
String get version => _version; String get version => _version;
@ -174,6 +212,9 @@ class InvenTreeAPI {
_version = data["version"]; _version = data["version"];
// Record the instance name of the server
instance = data['instance'] ?? '';
// Request token from the server if we do not already have one // Request token from the server if we do not already have one
if (_token.isNotEmpty) { if (_token.isNotEmpty) {
print("Already have token - $_token"); print("Already have token - $_token");

View File

@ -74,8 +74,13 @@ class InvenTreeModel {
} }
*/ */
Map<String, String> defaultListFilters() { return Map<String, String>(); }
// A map of "default" headers to use when performing a GET request
Map<String, String> defaultGetFilters() { return Map<String, String>(); }
// Return the detail view for the associated pk // Return the detail view for the associated pk
Future<InvenTreeModel> get(int pk) async { Future<InvenTreeModel> get(int pk, {Map<String, String> filters}) async {
// TODO - Add "timeout" // TODO - Add "timeout"
// TODO - Add error catching // TODO - Add error catching
@ -86,7 +91,18 @@ class InvenTreeModel {
addr += "/"; addr += "/";
} }
var response = await InvenTreeAPI().get(addr); var params = defaultGetFilters();
if (filters != null) {
// Override any default values
for (String key in filters.keys) {
params[key] = filters[key];
}
}
print("GET: $addr ${params.toString()}");
var response = await InvenTreeAPI().get(addr, params: params);
if (response.statusCode != 200) { if (response.statusCode != 200) {
print("Error retrieving data"); print("Error retrieving data");
@ -105,12 +121,20 @@ class InvenTreeModel {
filters = {}; filters = {};
} }
print("Listing endpoint: $URL"); var params = defaultListFilters();
if (filters != null) {
for (String key in filters.keys) {
params[key] = filters[key];
}
}
print("LIST: $URL ${params.toString()}");
// TODO - Add "timeout" // TODO - Add "timeout"
// TODO - Add error catching // TODO - Add error catching
var response = await InvenTreeAPI().get(URL, params:filters); var response = await InvenTreeAPI().get(URL, params:params);
// A list of "InvenTreeModel" items // A list of "InvenTreeModel" items
List<InvenTreeModel> results = new List<InvenTreeModel>(); List<InvenTreeModel> results = new List<InvenTreeModel>();

View File

@ -12,6 +12,15 @@ class InvenTreePartCategory extends InvenTreeModel {
@override @override
String URL = "part/category/"; String URL = "part/category/";
@override
Map<String, String> defaultListFilters() {
var filters = new Map<String, String>();
filters["active"] = "true";
return filters;
}
String get pathstring => jsondata['pathstring'] ?? ''; String get pathstring => jsondata['pathstring'] ?? '';
String get parentpathstring { String get parentpathstring {

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'model.dart'; import 'model.dart';
import 'package:InvenTree/api.dart'; import 'package:InvenTree/api.dart';
@ -6,15 +8,72 @@ class InvenTreeStockItem extends InvenTreeModel {
@override @override
String URL = "stock/"; String URL = "stock/";
@override
Map<String, String> defaultGetFilters() {
var headers = new Map<String, String>();
headers["part_detail"] = "true";
headers["location_detail"] = "true";
headers["supplier_detail"] = "true";
return headers;
}
InvenTreeStockItem() : super(); InvenTreeStockItem() : super();
InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) { InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
// TODO // TODO
} }
String get partName => jsondata['part__name'] as String ?? ''; int get partId => jsondata['part'] ?? -1;
String get partDescription => jsondata['part__description'] as String ?? ''; int get trackingItemCount => jsondata['tracking_items'] as int ?? 0;
String get partName {
String nm = '';
// Use the detailed part information as priority
if (jsondata.containsKey('part_detail')) {
nm = jsondata['part_detail']['full_name'] ?? '';
}
if (nm.isEmpty) {
nm = jsondata['part__name'] ?? '';
}
return nm;
}
String get partDescription {
String desc = '';
// Use the detailed part description as priority
if (jsondata.containsKey('part_detail')) {
desc = jsondata['part_detail']['description'] ?? '';
}
if (desc.isEmpty) {
desc = jsondata['part__description'] ?? '';
}
return desc;
}
String get partImage {
String img = '';
if (jsondata.containsKey('part_detail')) {
img = jsondata['part_detail']['thumbnail'] ?? '';
}
if (img.isEmpty) {
img = jsondata['part__thumbnail'] ?? '';
}
return img;
}
String get partThumbnail { String get partThumbnail {
String thumb = jsondata['part__thumbnail'] as String ?? ''; String thumb = jsondata['part__thumbnail'] as String ?? '';
@ -24,12 +83,58 @@ class InvenTreeStockItem extends InvenTreeModel {
return thumb; return thumb;
} }
int get supplierPartId => jsondata['supplier_part'] as int ?? -1;
String get supplierImage {
String thumb = '';
if (jsondata.containsKey("supplier_detail")) {
thumb = jsondata['supplier_detail']['supplier_logo'] ?? '';
}
return thumb;
}
String get supplierName {
String sname = '';
if (jsondata.containsKey("supplier_detail")) {
sname = jsondata["supplier_detail"]["supplier_name"] ?? '';
}
return sname;
}
String get supplierSKU {
String sku = '';
if (jsondata.containsKey("supplier_detail")) {
sku = jsondata["supplier_detail"]["SKU"] ?? '';
}
return sku;
}
int get serialNumber => jsondata['serial'] as int ?? null; int get serialNumber => jsondata['serial'] as int ?? null;
double get quantity => jsondata['quantity'] as double ?? 0.0; double get quantity => double.tryParse(jsondata['quantity'].toString() ?? '0');
int get locationId => jsondata['location'] as int ?? -1; int get locationId => jsondata['location'] as int ?? -1;
String get locationName {
String loc = '';
if (jsondata.containsKey('location_detail')) {
loc = jsondata['location_detail']['name'] ?? '';
}
if (loc.isEmpty) {
loc = jsondata['location__name'] ?? '';
}
return loc;
}
String get displayQuantity { String get displayQuantity {
// Display either quantity or serial number! // Display either quantity or serial number!

View File

@ -14,7 +14,7 @@ import 'barcode.dart';
import 'dart:convert'; import 'dart:convert';
import 'settings.dart'; import 'settings/settings.dart';
import 'api.dart'; import 'api.dart';
import 'preferences.dart'; import 'preferences.dart';
@ -151,6 +151,13 @@ class _MyHomePageState extends State<MyHomePage> {
_serverAddress = prefs.getString("server"); _serverAddress = prefs.getString("server");
// Reset the connection status variables
_serverStatus = "Connecting to server";
_serverMessage = "";
_serverConnection = false;
_serverIcon = new FaIcon(FontAwesomeIcons.spinner);
_serverStatusColor = Color.fromARGB(255, 50, 50, 250);
InvenTreeAPI().connect().then((bool result) { InvenTreeAPI().connect().then((bool result) {
if (result) { if (result) {
@ -176,21 +183,33 @@ class _MyHomePageState extends State<MyHomePage> {
onConnectFailure(fault); onConnectFailure(fault);
}); });
// Update widget state
setState(() {});
} }
void _search() { void _search() {
if (!InvenTreeAPI().checkConnection(context)) return;
// TODO // TODO
} }
void _scan() { void _scan() {
if (!InvenTreeAPI().checkConnection(context)) return;
scanQrCode(context); scanQrCode(context);
} }
void _parts() { void _parts() {
if (!InvenTreeAPI().checkConnection(context)) return;
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))); Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
} }
void _stock() { void _stock() {
if (!InvenTreeAPI().checkConnection(context)) return;
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null))); Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
} }
@ -349,6 +368,11 @@ class _MyHomePageState extends State<MyHomePage> {
style: TextStyle(color: _serverStatusColor), style: TextStyle(color: _serverStatusColor),
), ),
leading: _serverIcon, leading: _serverIcon,
onTap: () {
if (!_serverConnection) {
_checkServerConnection();
}
},
), ),
), ),
], ],

64
lib/settings/about.dart Normal file
View File

@ -0,0 +1,64 @@
import 'package:InvenTree/api.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:package_info/package_info.dart';
class InvenTreeAboutWidget extends StatelessWidget {
final PackageInfo info;
InvenTreeAboutWidget(this.info) : super();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("About InvenTree"),
),
body: ListView(
children: <Widget>[
ListTile(
title: Text("Server Address"),
subtitle: Text(InvenTreeAPI().baseUrl.isNotEmpty ? InvenTreeAPI().baseUrl : "Not connected"),
),
ListTile(
title: Text("Server Version"),
subtitle: Text(InvenTreeAPI().version.isNotEmpty ? InvenTreeAPI().version : "Not connected"),
),
ListTile(
title: Text("Server Instance"),
subtitle: Text(InvenTreeAPI().instance.isNotEmpty ? InvenTreeAPI().instance : "Not connected"),
),
Divider(),
ListTile(
title: Text("App Name"),
subtitle: Text("${info.appName}"),
),
ListTile(
title: Text("Package Name"),
subtitle: Text("${info.packageName}"),
),
ListTile(
title: Text("App Version"),
subtitle: Text("${info.version}"),
),
ListTile(
title: Text("Build Number"),
subtitle: Text("${info.buildNumber}")
),
Divider(),
ListTile(
title: Text("Submit Bug Report"),
subtitle: Text("https://github.com/inventree/inventree-app/issues/"),
onTap: () {
// TODO - Open the URL in an external webpage?
},
)
],
)
);
}
}

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'api.dart'; import '../api.dart';
import 'preferences.dart'; import '../preferences.dart';
class InvenTreeLoginSettingsWidget extends StatefulWidget { class InvenTreeLoginSettingsWidget extends StatefulWidget {
@ -81,7 +81,6 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
initialValue: _server, initialValue: _server,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "127.0.0.1:8000", hintText: "127.0.0.1:8000",
labelText: "Server:Port",
), ),
validator: _validateServer, validator: _validateServer,
onSaved: (String value) { onSaved: (String value) {
@ -89,7 +88,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
}, },
), ),
Divider(), Divider(),
Text("Login Details"), Text("Account Details"),
TextFormField( TextFormField(
initialValue: _username, initialValue: _username,
decoration: InputDecoration( decoration: InputDecoration(

View File

@ -1,8 +1,12 @@
import 'package:InvenTree/settings/about.dart';
import 'package:InvenTree/settings/login.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:InvenTree/api.dart'; import 'package:InvenTree/api.dart';
import 'login_settings.dart'; import 'login.dart';
import 'package:package_info/package_info.dart'; import 'package:package_info/package_info.dart';
@ -28,12 +32,14 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
ListTile( ListTile(
title: Text("Server Settings"), title: Text("Server Settings"),
subtitle: Text("Configure server and login settings"), subtitle: Text("Configure server and login settings"),
leading: FaIcon(FontAwesomeIcons.server),
onTap: _editServerSettings, onTap: _editServerSettings,
), ),
Divider(), Divider(),
ListTile( ListTile(
title: Text("About"), title: Text("About"),
subtitle: Text("App details"), subtitle: Text("App details"),
leading: FaIcon(FontAwesomeIcons.infoCircle),
onTap: _about, onTap: _about,
), ),
], ],
@ -52,40 +58,8 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
void _about() async { void _about() async {
PackageInfo.fromPlatform().then((PackageInfo info) { PackageInfo.fromPlatform().then((PackageInfo info) {
showDialog( Navigator.push(context,
context: context, MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info)));
child: new SimpleDialog(
title: new Text("About InvenTree"),
children: <Widget>[
ListTile(
title: Text("Server Version"),
subtitle: Text(InvenTreeAPI().version.isNotEmpty ? InvenTreeAPI().version : "Not connected"),
),
Divider(),
ListTile(
title: Text("App Name"),
subtitle: Text("${info.appName}"),
),
ListTile(
title: Text("Package Name"),
subtitle: Text("${info.packageName}"),
),
ListTile(
title: Text("App Version"),
subtitle: Text("${info.version}"),
),
ListTile(
title: Text("Build Number"),
subtitle: Text("${info.buildNumber}")
),
Divider(),
ListTile(
title: Text("Submit Bug Report"),
subtitle: Text("Submit a bug report or feature request at:\n https://github.com/inventree/inventree-app/issues/"),
)
]
),
);
}); });
} }
} }

View File

@ -169,7 +169,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
); );
}, },
body: SubcategoryList(_subcategories), body: SubcategoryList(_subcategories),
isExpanded: _subcategoriesExpanded, isExpanded: _subcategoriesExpanded && _subcategories.length > 0,
), ),
ExpansionPanel( ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) { headerBuilder: (BuildContext context, bool isExpanded) {
@ -185,7 +185,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
); );
}, },
body: PartList(_parts), body: PartList(_parts),
isExpanded: _partListExpanded, isExpanded: _partListExpanded && _parts.length > 0,
) )
], ],
), ),

View File

@ -1,10 +1,13 @@
import 'package:InvenTree/api.dart';
import 'package:InvenTree/barcode.dart'; import 'package:InvenTree/barcode.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:InvenTree/api.dart';
import 'package:InvenTree/widget/category_display.dart'; import 'package:InvenTree/widget/category_display.dart';
import 'package:InvenTree/widget/location_display.dart'; import 'package:InvenTree/widget/location_display.dart';
import 'package:InvenTree/settings.dart'; import 'package:InvenTree/settings/settings.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class InvenTreeDrawer extends StatelessWidget { class InvenTreeDrawer extends StatelessWidget {
@ -33,6 +36,7 @@ class InvenTreeDrawer extends StatelessWidget {
* Upon successful scan, data are passed off to be decoded. * Upon successful scan, data are passed off to be decoded.
*/ */
void _scan() async { void _scan() async {
if (!InvenTreeAPI().checkConnection(context)) return;
_closeDrawer(); _closeDrawer();
scanQrCode(context); scanQrCode(context);
@ -42,6 +46,7 @@ class InvenTreeDrawer extends StatelessWidget {
* Display the top-level PartCategory list * Display the top-level PartCategory list
*/ */
void _showParts() { void _showParts() {
if (!InvenTreeAPI().checkConnection(context)) return;
_closeDrawer(); _closeDrawer();
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))); Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
@ -51,6 +56,7 @@ class InvenTreeDrawer extends StatelessWidget {
* Display the top-level StockLocation list * Display the top-level StockLocation list
*/ */
void _showStock() { void _showStock() {
if (!InvenTreeAPI().checkConnection(context)) return;
_closeDrawer(); _closeDrawer();
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null))); Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
} }
@ -72,6 +78,7 @@ class InvenTreeDrawer extends StatelessWidget {
leading: new Image.asset( leading: new Image.asset(
"assets/image/icon.png", "assets/image/icon.png",
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
width: 40,
), ),
title: new Text("InvenTree"), title: new Text("InvenTree"),
onTap: _home, onTap: _home,

View File

@ -176,7 +176,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
); );
}, },
body: SublocationList(_sublocations), body: SublocationList(_sublocations),
isExpanded: _locationListExpanded, isExpanded: _locationListExpanded && _sublocations.length > 0,
), ),
ExpansionPanel( ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) { headerBuilder: (BuildContext context, bool isExpanded) {
@ -192,7 +192,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
); );
}, },
body: StockList(_items), body: StockList(_items),
isExpanded: _stockListExpanded, isExpanded: _stockListExpanded && _items.length > 0,
) )
] ]
), ),

View File

@ -29,158 +29,126 @@ class _PartDisplayState extends State<PartDisplayWidget> {
InvenTreePart part; InvenTreePart part;
/*
* Construct a list of detail elements about this part.
* Not all elements are set for each part, so only add the ones that are important.
*/
List<Widget> partDetails() {
List<Widget> widgets = [
// Image / name / description
ListTile(
title: Text("${part.fullname}"),
subtitle: Text("${part.description}"),
leading: Image(
image: InvenTreeAPI().getImage(part.image)
),
trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: null,
),
)
];
return widgets;
}
/* /*
* Build a list of tiles to display under the part description * Build a list of tiles to display under the part description
*/ */
List<Widget> partTiles() { List<Widget> partTiles() {
List<Widget> tiles = [ List<Widget> tiles = [];
// Image / name / description
tiles.add(
Card( Card(
child: Column( child: ListTile(
mainAxisSize: MainAxisSize.max, title: Text("${part.fullname}"),
mainAxisAlignment: MainAxisAlignment.spaceBetween, subtitle: Text("${part.description}"),
children: partDetails(), leading: Image(
) image: InvenTreeAPI().getImage(part.image)
), ),
]; trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: null,
),
)
)
);
// Category information // Category information
if (part.categoryName.isNotEmpty) { if (part.categoryName.isNotEmpty) {
tiles.add( tiles.add(
Card( ListTile(
child: ListTile(
title: Text("Part Category"), title: Text("Part Category"),
subtitle: Text("${part.categoryName}"), subtitle: Text("${part.categoryName}"),
leading: FaIcon(FontAwesomeIcons.stream), leading: FaIcon(FontAwesomeIcons.stream),
onTap: () { onTap: () {
InvenTreePartCategory().get(part.categoryId).then((var cat) { if (part.categoryId > 0) {
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat))); InvenTreePartCategory().get(part.categoryId).then((var cat) {
}); Navigator.push(context, MaterialPageRoute(
builder: (context) => CategoryDisplayWidget(cat)));
});
}
}, },
) )
)
); );
} }
// External link? // External link?
if (part.link.isNotEmpty) { if (part.link.isNotEmpty) {
tiles.add( tiles.add(
Card( ListTile(
child: ListTile(
title: Text("${part.link}"), title: Text("${part.link}"),
leading: FaIcon(FontAwesomeIcons.link), leading: FaIcon(FontAwesomeIcons.link),
trailing: Text(""), trailing: Text(""),
onTap: null, onTap: null,
) )
)
); );
} }
// Stock information // Stock information
tiles.add( tiles.add(
Card( ListTile(
child: ListTile(
title: Text("Stock"), title: Text("Stock"),
leading: FaIcon(FontAwesomeIcons.boxes), leading: FaIcon(FontAwesomeIcons.boxes),
trailing: Text("${part.inStock}"), trailing: Text("${part.inStock}"),
onTap: null, onTap: null,
), ),
)
); );
// Parts on order // Parts on order
if (part.isPurchaseable) { if (part.isPurchaseable) {
tiles.add( tiles.add(
Card( ListTile(
child: ListTile(
title: Text("On Order"), title: Text("On Order"),
leading: FaIcon(FontAwesomeIcons.shoppingCart), leading: FaIcon(FontAwesomeIcons.shoppingCart),
trailing: Text("${part.onOrder}"), trailing: Text("${part.onOrder}"),
onTap: null, onTap: null,
) )
)
); );
} }
// Parts being built // Parts being built
if (part.isAssembly) { if (part.isAssembly) {
tiles.add( tiles.add(ListTile(
Card(
child: ListTile(
title: Text("Bill of Materials"), title: Text("Bill of Materials"),
leading: FaIcon(FontAwesomeIcons.thList), leading: FaIcon(FontAwesomeIcons.thList),
trailing: Text("${part.bomItemCount}"), trailing: Text("${part.bomItemCount}"),
onTap: null, onTap: null,
) )
)
); );
tiles.add( tiles.add(
Card( ListTile(
child: ListTile(
title: Text("Building"), title: Text("Building"),
leading: FaIcon(FontAwesomeIcons.tools), leading: FaIcon(FontAwesomeIcons.tools),
trailing: Text("${part.building}"), trailing: Text("${part.building}"),
onTap: null, onTap: null,
) )
)
); );
} }
if (part.isComponent) { if (part.isComponent) {
tiles.add( tiles.add(ListTile(
Card(
child: ListTile(
title: Text("Used In"), title: Text("Used In"),
leading: FaIcon(FontAwesomeIcons.sitemap), leading: FaIcon(FontAwesomeIcons.sitemap),
trailing: Text("${part.usedInCount}"), trailing: Text("${part.usedInCount}"),
onTap: null, onTap: null,
) )
)
); );
} }
// Notes field? // Notes field?
if (part.notes.isNotEmpty) { if (part.notes.isNotEmpty) {
tiles.add( tiles.add(
Card( ListTile(
child: ListTile(
title: Text("Notes"), title: Text("Notes"),
leading: FaIcon(FontAwesomeIcons.stickyNote), leading: FaIcon(FontAwesomeIcons.stickyNote),
trailing: Text(""), trailing: Text(""),
onTap: null, onTap: null,
) )
)
); );
} }
tiles.add(Spacer());
return tiles; return tiles;
} }
@ -193,9 +161,7 @@ class _PartDisplayState extends State<PartDisplayWidget> {
), ),
drawer: new InvenTreeDrawer(context), drawer: new InvenTreeDrawer(context),
body: Center( body: Center(
child: Column( child: ListView(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: partTiles(), children: partTiles(),
), ),
) )

View File

@ -1,10 +1,16 @@
import 'package:InvenTree/inventree/stock.dart'; import 'package:InvenTree/inventree/stock.dart';
import 'package:InvenTree/inventree/part.dart';
import 'package:InvenTree/widget/location_display.dart';
import 'package:InvenTree/widget/part_display.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:InvenTree/api.dart';
import 'package:InvenTree/widget/drawer.dart'; import 'package:InvenTree/widget/drawer.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class StockItemDisplayWidget extends StatefulWidget { class StockItemDisplayWidget extends StatefulWidget {
@ -25,27 +31,137 @@ class _StockItemDisplayState extends State<StockItemDisplayWidget> {
final InvenTreeStockItem item; final InvenTreeStockItem item;
String get _title { /*
if (item == null) { * Construct a list of detail elements about this StockItem.
return "Stock Item"; * The number of elements may vary depending on the StockItem details
} else { */
return "Item: x ${item.partName}"; List<Widget> stockTiles() {
List<Widget> tiles = [];
// Image / name / description
tiles.add(
Card(
child: ListTile(
title: Text("${item.partName}"),
subtitle: Text("${item.partDescription}"),
leading: Image(
image: InvenTreeAPI().getImage(item.partImage),
),
trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: null,
)
)
)
);
tiles.add(
ListTile(
title: Text("Part"),
subtitle: Text("${item.partName}"),
leading: FaIcon(FontAwesomeIcons.shapes),
onTap: () {
if (item.partId > 0) {
InvenTreePart().get(item.partId).then((var part) {
if (part is InvenTreePart) {
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDisplayWidget(part)));
}
});
}
},
)
);
// Quantity information
tiles.add(
ListTile(
title: Text("Quantity"),
leading: FaIcon(FontAwesomeIcons.cubes),
trailing: Text("${item.quantity}"),
)
);
// Location information
if (item.locationName.isNotEmpty) {
tiles.add(
ListTile(
title: Text("Stock Location"),
subtitle: Text("${item.locationName}"),
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
onTap: () {
if (item.locationId > 0) {
InvenTreeStockLocation().get(item.locationId).then((var loc) {
Navigator.push(context, MaterialPageRoute(
builder: (context) => LocationDisplayWidget(loc)));
});
}
},
)
);
} }
// Supplier part?
if (item.supplierPartId > 0) {
tiles.add(
ListTile(
title: Text("${item.supplierName}"),
subtitle: Text("${item.supplierSKU}"),
leading: FaIcon(FontAwesomeIcons.industry),
trailing: Image(
image: InvenTreeAPI().getImage(item.supplierImage),
height: 32,
),
onTap: null,
)
);
}
if (item.link.isNotEmpty) {
tiles.add(
ListTile(
title: Text("${item.link}"),
leading: FaIcon(FontAwesomeIcons.link),
trailing: Text(""),
onTap: null,
)
);
}
if (item.trackingItemCount > 0) {
tiles.add(
ListTile(
title: Text("History"),
leading: FaIcon(FontAwesomeIcons.history),
trailing: Text("${item.trackingItemCount}"),
onTap: null,
)
);
}
if (item.notes.isNotEmpty) {
tiles.add(
ListTile(
title: Text("Notes"),
leading: FaIcon(FontAwesomeIcons.stickyNote),
trailing: Text(""),
onTap: null,
)
);
}
return tiles;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_title), title: Text("Stock Item"),
), ),
drawer: new InvenTreeDrawer(context), drawer: new InvenTreeDrawer(context),
body: Center( body: Center(
child: Column( child: ListView(
mainAxisAlignment: MainAxisAlignment.center, children: stockTiles(),
children: <Widget>[
Text("Stock Item: hello"),
],
) )
) )
); );