mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
Merge pull request #19 from SchrodingersGat/stock-item-page
Improve view for StockItem
This commit is contained in:
commit
c7fbe99f53
43
lib/api.dart
43
lib/api.dart
@ -72,8 +72,46 @@ class InvenTreeAPI {
|
||||
// Authentication token (initially empty, must be requested)
|
||||
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
|
||||
String _version;
|
||||
String _version = '';
|
||||
|
||||
// Getter for server version information
|
||||
String get version => _version;
|
||||
@ -174,6 +212,9 @@ class InvenTreeAPI {
|
||||
|
||||
_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
|
||||
if (_token.isNotEmpty) {
|
||||
print("Already have token - $_token");
|
||||
|
@ -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
|
||||
Future<InvenTreeModel> get(int pk) async {
|
||||
Future<InvenTreeModel> get(int pk, {Map<String, String> filters}) async {
|
||||
|
||||
// TODO - Add "timeout"
|
||||
// TODO - Add error catching
|
||||
@ -86,7 +91,18 @@ class InvenTreeModel {
|
||||
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) {
|
||||
print("Error retrieving data");
|
||||
@ -105,12 +121,20 @@ class InvenTreeModel {
|
||||
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 error catching
|
||||
|
||||
var response = await InvenTreeAPI().get(URL, params:filters);
|
||||
var response = await InvenTreeAPI().get(URL, params:params);
|
||||
|
||||
// A list of "InvenTreeModel" items
|
||||
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
||||
|
@ -12,6 +12,15 @@ class InvenTreePartCategory extends InvenTreeModel {
|
||||
@override
|
||||
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 parentpathstring {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'model.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
@ -6,15 +8,72 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
@override
|
||||
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.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
|
||||
// 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 thumb = jsondata['part__thumbnail'] as String ?? '';
|
||||
@ -24,12 +83,58 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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 {
|
||||
// Display either quantity or serial number!
|
||||
|
||||
|
@ -14,7 +14,7 @@ import 'barcode.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'settings.dart';
|
||||
import 'settings/settings.dart';
|
||||
import 'api.dart';
|
||||
import 'preferences.dart';
|
||||
|
||||
@ -151,6 +151,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
|
||||
_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) {
|
||||
|
||||
if (result) {
|
||||
@ -176,21 +183,33 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
|
||||
onConnectFailure(fault);
|
||||
});
|
||||
|
||||
// Update widget state
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _search() {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
void _scan() {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
|
||||
scanQrCode(context);
|
||||
}
|
||||
|
||||
void _parts() {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||
}
|
||||
|
||||
void _stock() {
|
||||
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||
}
|
||||
|
||||
@ -349,6 +368,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
style: TextStyle(color: _serverStatusColor),
|
||||
),
|
||||
leading: _serverIcon,
|
||||
onTap: () {
|
||||
if (!_serverConnection) {
|
||||
_checkServerConnection();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
64
lib/settings/about.dart
Normal file
64
lib/settings/about.dart
Normal 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?
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'api.dart';
|
||||
import 'preferences.dart';
|
||||
import '../api.dart';
|
||||
import '../preferences.dart';
|
||||
|
||||
class InvenTreeLoginSettingsWidget extends StatefulWidget {
|
||||
|
||||
@ -81,7 +81,6 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
initialValue: _server,
|
||||
decoration: InputDecoration(
|
||||
hintText: "127.0.0.1:8000",
|
||||
labelText: "Server:Port",
|
||||
),
|
||||
validator: _validateServer,
|
||||
onSaved: (String value) {
|
||||
@ -89,7 +88,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
},
|
||||
),
|
||||
Divider(),
|
||||
Text("Login Details"),
|
||||
Text("Account Details"),
|
||||
TextFormField(
|
||||
initialValue: _username,
|
||||
decoration: InputDecoration(
|
@ -1,8 +1,12 @@
|
||||
import 'package:InvenTree/settings/about.dart';
|
||||
import 'package:InvenTree/settings/login.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
import 'login_settings.dart';
|
||||
import 'login.dart';
|
||||
|
||||
import 'package:package_info/package_info.dart';
|
||||
|
||||
@ -28,12 +32,14 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
|
||||
ListTile(
|
||||
title: Text("Server Settings"),
|
||||
subtitle: Text("Configure server and login settings"),
|
||||
leading: FaIcon(FontAwesomeIcons.server),
|
||||
onTap: _editServerSettings,
|
||||
),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text("About"),
|
||||
subtitle: Text("App details"),
|
||||
leading: FaIcon(FontAwesomeIcons.infoCircle),
|
||||
onTap: _about,
|
||||
),
|
||||
],
|
||||
@ -52,40 +58,8 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
|
||||
void _about() async {
|
||||
|
||||
PackageInfo.fromPlatform().then((PackageInfo info) {
|
||||
showDialog(
|
||||
context: context,
|
||||
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/"),
|
||||
)
|
||||
]
|
||||
),
|
||||
);
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info)));
|
||||
});
|
||||
}
|
||||
}
|
@ -169,7 +169,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
);
|
||||
},
|
||||
body: SubcategoryList(_subcategories),
|
||||
isExpanded: _subcategoriesExpanded,
|
||||
isExpanded: _subcategoriesExpanded && _subcategories.length > 0,
|
||||
),
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
@ -185,7 +185,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
||||
);
|
||||
},
|
||||
body: PartList(_parts),
|
||||
isExpanded: _partListExpanded,
|
||||
isExpanded: _partListExpanded && _parts.length > 0,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'package:InvenTree/api.dart';
|
||||
import 'package:InvenTree/barcode.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
|
||||
import 'package:InvenTree/widget/category_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';
|
||||
|
||||
class InvenTreeDrawer extends StatelessWidget {
|
||||
@ -33,6 +36,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
||||
* Upon successful scan, data are passed off to be decoded.
|
||||
*/
|
||||
void _scan() async {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
|
||||
_closeDrawer();
|
||||
scanQrCode(context);
|
||||
@ -42,6 +46,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
||||
* Display the top-level PartCategory list
|
||||
*/
|
||||
void _showParts() {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
|
||||
_closeDrawer();
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||
@ -51,6 +56,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
||||
* Display the top-level StockLocation list
|
||||
*/
|
||||
void _showStock() {
|
||||
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||
_closeDrawer();
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||
}
|
||||
@ -72,6 +78,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
||||
leading: new Image.asset(
|
||||
"assets/image/icon.png",
|
||||
fit: BoxFit.scaleDown,
|
||||
width: 40,
|
||||
),
|
||||
title: new Text("InvenTree"),
|
||||
onTap: _home,
|
||||
|
@ -176,7 +176,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
);
|
||||
},
|
||||
body: SublocationList(_sublocations),
|
||||
isExpanded: _locationListExpanded,
|
||||
isExpanded: _locationListExpanded && _sublocations.length > 0,
|
||||
),
|
||||
ExpansionPanel(
|
||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||
@ -192,7 +192,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
||||
);
|
||||
},
|
||||
body: StockList(_items),
|
||||
isExpanded: _stockListExpanded,
|
||||
isExpanded: _stockListExpanded && _items.length > 0,
|
||||
)
|
||||
]
|
||||
),
|
||||
|
@ -29,158 +29,126 @@ class _PartDisplayState extends State<PartDisplayWidget> {
|
||||
|
||||
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
|
||||
*/
|
||||
List<Widget> partTiles() {
|
||||
|
||||
List<Widget> tiles = [
|
||||
List<Widget> tiles = [];
|
||||
|
||||
// Image / name / description
|
||||
tiles.add(
|
||||
Card(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: partDetails(),
|
||||
)
|
||||
),
|
||||
];
|
||||
child: ListTile(
|
||||
title: Text("${part.fullname}"),
|
||||
subtitle: Text("${part.description}"),
|
||||
leading: Image(
|
||||
image: InvenTreeAPI().getImage(part.image)
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: FaIcon(FontAwesomeIcons.edit),
|
||||
onPressed: null,
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Category information
|
||||
if (part.categoryName.isNotEmpty) {
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
ListTile(
|
||||
title: Text("Part Category"),
|
||||
subtitle: Text("${part.categoryName}"),
|
||||
leading: FaIcon(FontAwesomeIcons.stream),
|
||||
onTap: () {
|
||||
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
});
|
||||
if (part.categoryId > 0) {
|
||||
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// External link?
|
||||
if (part.link.isNotEmpty) {
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
ListTile(
|
||||
title: Text("${part.link}"),
|
||||
leading: FaIcon(FontAwesomeIcons.link),
|
||||
trailing: Text(""),
|
||||
onTap: null,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Stock information
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
ListTile(
|
||||
title: Text("Stock"),
|
||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||
trailing: Text("${part.inStock}"),
|
||||
onTap: null,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Parts on order
|
||||
if (part.isPurchaseable) {
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
ListTile(
|
||||
title: Text("On Order"),
|
||||
leading: FaIcon(FontAwesomeIcons.shoppingCart),
|
||||
trailing: Text("${part.onOrder}"),
|
||||
onTap: null,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Parts being built
|
||||
if (part.isAssembly) {
|
||||
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
tiles.add(ListTile(
|
||||
title: Text("Bill of Materials"),
|
||||
leading: FaIcon(FontAwesomeIcons.thList),
|
||||
trailing: Text("${part.bomItemCount}"),
|
||||
onTap: null,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
ListTile(
|
||||
title: Text("Building"),
|
||||
leading: FaIcon(FontAwesomeIcons.tools),
|
||||
trailing: Text("${part.building}"),
|
||||
onTap: null,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (part.isComponent) {
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
tiles.add(ListTile(
|
||||
title: Text("Used In"),
|
||||
leading: FaIcon(FontAwesomeIcons.sitemap),
|
||||
trailing: Text("${part.usedInCount}"),
|
||||
onTap: null,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Notes field?
|
||||
if (part.notes.isNotEmpty) {
|
||||
tiles.add(
|
||||
Card(
|
||||
child: ListTile(
|
||||
ListTile(
|
||||
title: Text("Notes"),
|
||||
leading: FaIcon(FontAwesomeIcons.stickyNote),
|
||||
trailing: Text(""),
|
||||
onTap: null,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
tiles.add(Spacer());
|
||||
|
||||
return tiles;
|
||||
|
||||
}
|
||||
@ -193,9 +161,7 @@ class _PartDisplayState extends State<PartDisplayWidget> {
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
child: ListView(
|
||||
children: partTiles(),
|
||||
),
|
||||
)
|
||||
|
@ -1,10 +1,16 @@
|
||||
|
||||
|
||||
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/material.dart';
|
||||
|
||||
import 'package:InvenTree/api.dart';
|
||||
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class StockItemDisplayWidget extends StatefulWidget {
|
||||
|
||||
@ -25,27 +31,137 @@ class _StockItemDisplayState extends State<StockItemDisplayWidget> {
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
String get _title {
|
||||
if (item == null) {
|
||||
return "Stock Item";
|
||||
} else {
|
||||
return "Item: x ${item.partName}";
|
||||
/*
|
||||
* Construct a list of detail elements about this StockItem.
|
||||
* The number of elements may vary depending on the StockItem details
|
||||
*/
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_title),
|
||||
title: Text("Stock Item"),
|
||||
),
|
||||
drawer: new InvenTreeDrawer(context),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text("Stock Item: hello"),
|
||||
],
|
||||
child: ListView(
|
||||
children: stockTiles(),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user