2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 13:36:50 +00:00

Merge branch 'edit-item'

This commit is contained in:
Oliver Walters 2021-01-21 20:59:32 +11:00
commit fc6e90ce0b
7 changed files with 145 additions and 78 deletions

View File

@ -380,7 +380,7 @@ class InvenTreeAPI {
* Load image from the InvenTree server, * Load image from the InvenTree server,
* or from local cache (if it has been cached!) * or from local cache (if it has been cached!)
*/ */
CachedNetworkImage getImage(String imageUrl) { CachedNetworkImage getImage(String imageUrl, {double height, double width}) {
if (imageUrl.isEmpty) { if (imageUrl.isEmpty) {
imageUrl = staticImage; imageUrl = staticImage;
} }
@ -392,6 +392,8 @@ class InvenTreeAPI {
placeholder: (context, url) => CircularProgressIndicator(), placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(FontAwesomeIcons.exclamation), errorWidget: (context, url, error) => Icon(FontAwesomeIcons.exclamation),
httpHeaders: defaultHeaders(), httpHeaders: defaultHeaders(),
height: height,
width: width,
); );
} }
} }

View File

@ -47,13 +47,15 @@ class InvenTreeModel {
String get description => jsondata['description'] ?? ''; 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" // Legacy API provided external link as "URL", while newer API uses "link"
String get link => jsondata['link'] ?? jsondata['URL'] ?? ''; String get link => jsondata['link'] ?? jsondata['URL'] ?? '';
String get keywords => jsondata['keywords'] as String ?? '';
// Create a new object from JSON data (not a constructor!) // Create a new object from JSON data (not a constructor!)
InvenTreeModel createFromJson(Map<String, dynamic> json) { InvenTreeModel createFromJson(Map<String, dynamic> json) {

View File

@ -31,6 +31,17 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
@override @override
String getAppBarTitle(BuildContext context) { return "Part Category"; } String getAppBarTitle(BuildContext context) { return "Part Category"; }
@override
List<Widget> getAppBarActions(BuildContext context) {
return <Widget>[
IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
tooltip: 'Edit',
onPressed: null,
)
];
}
_CategoryDisplayState(this.category) {} _CategoryDisplayState(this.category) {}
// The local InvenTreePartCategory object // The local InvenTreePartCategory object
@ -94,10 +105,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
ListTile( ListTile(
title: Text("${category.name}"), title: Text("${category.name}"),
subtitle: Text("${category.description}"), subtitle: Text("${category.description}"),
trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: null,
),
), ),
ListTile( ListTile(
title: Text("Parent Category"), title: Text("Parent Category"),
@ -260,7 +267,11 @@ class PartList extends StatelessWidget {
return ListTile( return ListTile(
title: Text("${part.name}"), title: Text("${part.name}"),
subtitle: Text("${part.description}"), subtitle: Text("${part.description}"),
leading: InvenTreeAPI().getImage(part.thumbnail), leading: InvenTreeAPI().getImage(
part.thumbnail,
width: 40,
height: 40,
),
onTap: () { onTap: () {
_openPart(context, part.pk); _openPart(context, part.pk);
}, },

View File

@ -28,6 +28,18 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
@override @override
String getAppBarTitle(BuildContext context) { return "Stock Location"; } String getAppBarTitle(BuildContext context) { return "Stock Location"; }
@override
List<Widget> getAppBarActions(BuildContext context) {
return <Widget>[
IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
tooltip: "Edit",
// TODO - Edit stock location
onPressed: null,
)
];
}
_LocationDisplayState(this.location) {} _LocationDisplayState(this.location) {}
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>(); List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
@ -98,10 +110,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
ListTile( ListTile(
title: Text("${location.name}"), title: Text("${location.name}"),
subtitle: Text("${location.description}"), subtitle: Text("${location.description}"),
trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: null,
),
), ),
ListTile( ListTile(
title: Text("Parent Category"), title: Text("Parent Category"),
@ -245,7 +253,11 @@ class StockList extends StatelessWidget {
return ListTile( return ListTile(
title: Text("${item.partName}"), title: Text("${item.partName}"),
subtitle: Text("${item.partDescription}"), subtitle: Text("${item.partDescription}"),
leading: InvenTreeAPI().getImage(item.partThumbnail), leading: InvenTreeAPI().getImage(
item.partThumbnail,
width: 40,
height: 40,
),
trailing: Text("${item.displayQuantity}", trailing: Text("${item.displayQuantity}",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),

View File

@ -32,6 +32,18 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
@override @override
String getAppBarTitle(BuildContext context) { return "Part"; } String getAppBarTitle(BuildContext context) { return "Part"; }
@override
List<Widget> getAppBarActions(BuildContext context) {
return <Widget>[
// TODO: Hide the 'edit' button if the user does not have permission!!
IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
tooltip: 'Edit',
onPressed: _editPartDialog,
)
];
}
_PartDisplayState(this.part) { _PartDisplayState(this.part) {
// TODO // TODO
} }
@ -75,6 +87,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
var _description; var _description;
var _ipn; var _ipn;
var _revision; var _revision;
var _keywords;
showFormDialog(context, "Edit Part", showFormDialog(context, "Edit Part",
key: _editPartKey, key: _editPartKey,
@ -95,6 +108,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
"name": _name, "name": _name,
"description": _description, "description": _description,
"IPN": _ipn, "IPN": _ipn,
"keywords": _keywords,
}); });
} }
}, },
@ -114,7 +128,14 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
StringField( StringField(
label: "Internal Part Number", label: "Internal Part Number",
initial: part.IPN, initial: part.IPN,
allowEmpty: true,
onSaved: (value) => _ipn = value, onSaved: (value) => _ipn = value,
),
StringField(
label: "Keywords",
initial: part.keywords,
allowEmpty: true,
onSaved: (value) => _keywords = value,
) )
] ]
@ -136,10 +157,6 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
title: Text("${part.fullname}"), title: Text("${part.fullname}"),
subtitle: Text("${part.description}"), subtitle: Text("${part.description}"),
leading: InvenTreeAPI().getImage(part.thumbnail), leading: InvenTreeAPI().getImage(part.thumbnail),
trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: _editPartDialog,
),
) )
) )
); );
@ -174,37 +191,25 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
); );
} }
// External link?
if (part.link.isNotEmpty) {
tiles.add(
ListTile(
title: Text("${part.link}"),
leading: FaIcon(FontAwesomeIcons.link),
trailing: Text(""),
onTap: null,
)
);
}
// Stock information // Stock information
tiles.add( tiles.add(
ListTile( ListTile(
title: Text("Stock"), title: Text("Stock"),
leading: FaIcon(FontAwesomeIcons.boxes), leading: FaIcon(FontAwesomeIcons.boxes),
trailing: Text("${part.inStock}"), trailing: Text("${part.inStock}"),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => PartStockDetailWidget(part)) MaterialPageRoute(builder: (context) => PartStockDetailWidget(part))
); );
}, },
), ),
); );
// Parts on order // Parts on order
if (part.isPurchaseable) { if (part.isPurchaseable) {
tiles.add( tiles.add(
ListTile( 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}"),
@ -218,11 +223,11 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
if (false && part.isAssembly) { if (false && part.isAssembly) {
tiles.add(ListTile( tiles.add(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(
@ -238,9 +243,31 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
// TODO - Do we want to use the app to display "used in"? // TODO - Do we want to use the app to display "used in"?
if (false && part.isComponent) { if (false && part.isComponent) {
tiles.add(ListTile( tiles.add(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,
)
);
}
// 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, onTap: null,
) )
); );

View File

@ -11,6 +11,10 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
// Storage for context once "Build" is called // Storage for context once "Build" is called
BuildContext context; BuildContext context;
List<Widget> getAppBarActions(BuildContext context) {
return [];
}
String getAppBarTitle(BuildContext context) { return "App Bar Title"; } String getAppBarTitle(BuildContext context) { return "App Bar Title"; }
void initState() { void initState() {
@ -37,6 +41,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
AppBar getAppBar(BuildContext context) { AppBar getAppBar(BuildContext context) {
return AppBar( return AppBar(
title: Text(getAppBarTitle(context)), title: Text(getAppBarTitle(context)),
actions: getAppBarActions(context),
); );
} }

View File

@ -37,6 +37,18 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
@override @override
String getAppBarTitle(BuildContext context) { return "Stock Item"; } String getAppBarTitle(BuildContext context) { return "Stock Item"; }
@override
List<Widget> getAppBarActions(BuildContext context) {
return <Widget>[
// 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 _quantityController = TextEditingController();
final TextEditingController _notesController = TextEditingController(); final TextEditingController _notesController = TextEditingController();
@ -333,10 +345,6 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
title: Text("${item.partName}"), title: Text("${item.partName}"),
subtitle: Text("${item.partDescription}"), subtitle: Text("${item.partDescription}"),
leading: InvenTreeAPI().getImage(item.partImage), leading: InvenTreeAPI().getImage(item.partImage),
trailing: IconButton(
icon: FaIcon(FontAwesomeIcons.edit),
onPressed: _editStockItemDialog,
)
) )
) )
); );
@ -358,6 +366,33 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
) )
); );
// 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 // Quantity information
if (item.isSerialized()) { if (item.isSerialized()) {
tiles.add( tiles.add(
@ -391,33 +426,6 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
) )
); );
// 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? // Supplier part?
if (item.supplierPartId > 0) { if (item.supplierPartId > 0) {
tiles.add( tiles.add(