279 lines
9 KiB
Dart
279 lines
9 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, 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;
|
||
|
|
||
|
// 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;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
|
||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
|
fetchCategories();
|
||
|
fetchProducts();
|
||
|
|
||
|
if (widget.item != null) {
|
||
|
fetchItem();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(
|
||
|
title: Text((widget.item == null)
|
||
|
? AppLocalizations.of(context)!.createItem
|
||
|
: 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>();
|
||
|
|
||
|
if (widget.item == null) {
|
||
|
doNetworkRequest(scaffMgr,
|
||
|
req: () => postWithCreadentials(
|
||
|
credentials: user,
|
||
|
target: user.server,
|
||
|
path: 'addItem',
|
||
|
body: {
|
||
|
'room': widget.room,
|
||
|
'server': widget.server,
|
||
|
'state': 0,
|
||
|
'title': _ctrName.text,
|
||
|
'description': _ctrDescription.text,
|
||
|
'listCatID': _ctrCategory,
|
||
|
'unit': _ctrUnit,
|
||
|
'value': _ctrValue,
|
||
|
'listProdID': _ctrLink
|
||
|
}),
|
||
|
onOK: (_) async {
|
||
|
nav.pop();
|
||
|
});
|
||
|
} else {
|
||
|
doNetworkRequest(scaffMgr,
|
||
|
req: () => postWithCreadentials(
|
||
|
credentials: user,
|
||
|
target: user.server,
|
||
|
path: 'changeItem',
|
||
|
body: {
|
||
|
'listItemID': widget.item,
|
||
|
'room': widget.room,
|
||
|
'server': widget.server,
|
||
|
'title': _ctrName.text,
|
||
|
'description': _ctrDescription.text,
|
||
|
'listCatID': _ctrCategory,
|
||
|
'defUnit': _ctrUnit,
|
||
|
'defValue': _ctrValue,
|
||
|
'listProdID': _ctrLink
|
||
|
}),
|
||
|
onOK: (_) async {
|
||
|
nav.pop();
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
label: Text(widget.item != null
|
||
|
? AppLocalizations.of(context)!.editItemShort
|
||
|
: AppLocalizations.of(context)!.createItemShort),
|
||
|
icon: Icon(widget.item != null ? Icons.edit : Icons.add)),
|
||
|
);
|
||
|
}
|
||
|
}
|