384fbb0573
The edit item screen might be overwhelming at first, and if you only want to add simple items (by name) to the list, it is way easier to simply type the name and click create to create a simple item. After creating the item the user will be redirected (history replacement) to the edit screen, but clicking back will bring them back to the list. This screen also makes linking products easier and allows the user to create new products if they notice they are using the same item multiple times or can't be bothered to switch to the products tab
262 lines
9.3 KiB
Dart
262 lines
9.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
import 'package:outbag_app/backend/request.dart';
|
|
import 'package:outbag_app/backend/room.dart';
|
|
import 'package:outbag_app/backend/user.dart';
|
|
import 'package:outbag_app/components/category_picker.dart';
|
|
import 'package:outbag_app/components/product_picker.dart';
|
|
import 'package:outbag_app/components/value_unit_input.dart';
|
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
|
import 'package:outbag_app/tools/snackbar.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
class EditItemPage extends StatefulWidget {
|
|
final String room;
|
|
final String server;
|
|
|
|
final int item;
|
|
|
|
const EditItemPage(
|
|
{super.key,
|
|
required this.room,
|
|
required this.server,
|
|
required this.item});
|
|
|
|
@override
|
|
State<StatefulWidget> createState() => _EditItemPageState();
|
|
}
|
|
|
|
class _EditItemPageState extends State<EditItemPage> {
|
|
// input controllers
|
|
final _ctrName = TextEditingController();
|
|
final _ctrDescription = TextEditingController();
|
|
int? _ctrCategory;
|
|
int _ctrUnit = 0;
|
|
String _ctrValue = '';
|
|
int? _ctrLink;
|
|
int _ctrState = 0;
|
|
|
|
// data cache
|
|
List<RoomCategory> categories = [];
|
|
List<RoomProduct> products = [];
|
|
RoomItem? item;
|
|
|
|
void fetchCategories() {
|
|
final user = context.read<User>();
|
|
|
|
// TODO: load cached categories first
|
|
|
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
|
req: () => postWithCreadentials(
|
|
credentials: user,
|
|
target: user.server,
|
|
path: 'getCategories',
|
|
body: {'room': widget.room, 'server': widget.server}),
|
|
onOK: (body) async {
|
|
final resp = body['data']
|
|
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
|
|
.toList();
|
|
|
|
setState(() {
|
|
categories = resp;
|
|
});
|
|
});
|
|
}
|
|
|
|
void fetchProducts() {
|
|
final user = context.read<User>();
|
|
|
|
// TODO: load cached products first
|
|
|
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
|
req: () => postWithCreadentials(
|
|
credentials: user,
|
|
target: user.server,
|
|
path: 'getProducts',
|
|
body: {'room': widget.room, 'server': widget.server}),
|
|
onOK: (body) async {
|
|
final resp = body['data']
|
|
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
|
|
.toList();
|
|
|
|
setState(() {
|
|
products = resp;
|
|
});
|
|
});
|
|
}
|
|
|
|
void fetchItem() {
|
|
final user = context.read<User>();
|
|
|
|
// TODO: load cached item first
|
|
|
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
|
req: () => postWithCreadentials(
|
|
credentials: user,
|
|
target: user.server,
|
|
path: 'getItem',
|
|
body: {
|
|
'room': widget.room,
|
|
'server': widget.server,
|
|
'listItemID': widget.item
|
|
}),
|
|
onOK: (body) async {
|
|
final resp = RoomItem.fromJSON(body['data']);
|
|
setState(() {
|
|
item = resp;
|
|
_ctrName.text = resp.name;
|
|
_ctrDescription.text = resp.description;
|
|
_ctrValue = resp.value;
|
|
_ctrCategory = resp.category;
|
|
_ctrLink = resp.link;
|
|
_ctrUnit = resp.unit;
|
|
_ctrState = resp.state;
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
fetchCategories();
|
|
fetchProducts();
|
|
|
|
fetchItem();
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(AppLocalizations.of(context)!.editItem),
|
|
),
|
|
body: SingleChildScrollView(
|
|
child: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(14),
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 400),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrName,
|
|
keyboardType: TextInputType.name,
|
|
decoration: InputDecoration(
|
|
prefixIcon: const Icon(Icons.badge),
|
|
labelText: AppLocalizations.of(context)!
|
|
.inputItemNameLabel,
|
|
hintText: AppLocalizations.of(context)!
|
|
.inputItemNameHint,
|
|
helperText: AppLocalizations.of(context)!
|
|
.inputItemNameHelp,
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
ProductPicker(
|
|
label: AppLocalizations.of(context)!
|
|
.selectLinkedProductLabel,
|
|
hint: AppLocalizations.of(context)!
|
|
.selectLinkedProductHint,
|
|
products: products,
|
|
selected: _ctrLink,
|
|
onSelect: (pid) {
|
|
setState(() {
|
|
_ctrLink = pid;
|
|
});
|
|
},
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TextField(
|
|
controller: _ctrDescription,
|
|
keyboardType: TextInputType.text,
|
|
decoration: InputDecoration(
|
|
labelText: AppLocalizations.of(context)!
|
|
.inputItemDescriptionLabel,
|
|
hintText: AppLocalizations.of(context)!
|
|
.inputItemDescriptionHint,
|
|
helperText: AppLocalizations.of(context)!
|
|
.inputItemDescriptionHelp,
|
|
prefixIcon: const Icon(Icons.dns),
|
|
border: const OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
DynamicValueUnitInput(
|
|
initialUnit: _ctrUnit,
|
|
initialValue: _ctrValue,
|
|
onUnitChange: (unit) {
|
|
setState(() {
|
|
_ctrUnit = unit;
|
|
});
|
|
},
|
|
onValueChange: (value) {
|
|
setState(() {
|
|
_ctrValue = value;
|
|
});
|
|
},
|
|
),
|
|
CategoryPicker(
|
|
label: AppLocalizations.of(context)!
|
|
.selectCategoryLabel,
|
|
hint: AppLocalizations.of(context)!
|
|
.selectCategoryHint,
|
|
categories: categories,
|
|
selected: _ctrCategory,
|
|
onSelect: (cid) {
|
|
setState(() {
|
|
_ctrCategory = cid;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
))))),
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
onPressed: () async {
|
|
final scaffMgr = ScaffoldMessenger.of(context);
|
|
final trans = AppLocalizations.of(context);
|
|
final nav = Navigator.of(context);
|
|
|
|
if (_ctrName.text.isEmpty) {
|
|
showSimpleSnackbar(scaffMgr,
|
|
text: trans!.errorProductNameShouldNotBeEmpty,
|
|
action: trans.ok);
|
|
return;
|
|
}
|
|
|
|
final user = context.read<User>();
|
|
|
|
doNetworkRequest(scaffMgr,
|
|
req: () => postWithCreadentials(
|
|
credentials: user,
|
|
target: user.server,
|
|
path: 'changeItem',
|
|
body: {
|
|
'listItemID': widget.item,
|
|
'room': widget.room,
|
|
'server': widget.server,
|
|
'title': _ctrName.text,
|
|
'state': _ctrState,
|
|
'description': _ctrDescription.text,
|
|
'listCatID': _ctrCategory,
|
|
'unit': _ctrUnit,
|
|
'value': _ctrValue,
|
|
'listProdID': _ctrLink
|
|
}),
|
|
onOK: (_) async {
|
|
nav.pop();
|
|
});
|
|
},
|
|
label: Text(AppLocalizations.of(context)!.editItemShort),
|
|
icon: const Icon(Icons.edit)),
|
|
);
|
|
}
|
|
}
|