actions-test/lib/screens/room/items/new.dart
Jakob Meier 7174a03cf2
cache itemss for items list and edit item screen
BUG: when deleting an item the item list won't update
2024-02-23 20:44:42 +01:00

321 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:go_router/go_router.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_chip.dart';
import 'package:outbag_app/tools/fetch_wrapper.dart';
import 'package:provider/provider.dart';
class NewItemPage extends StatefulWidget {
final String room;
final String server;
final int? item;
const NewItemPage(
{super.key, required this.room, required this.server, this.item});
@override
State<StatefulWidget> createState() => _NewItemPageState();
}
class _NewItemPageState extends State<NewItemPage> {
// input controllers
final _ctrInput = TextEditingController();
// data cache
List<RoomCategory> categories = [];
List<RoomProduct> products = [];
void fetchCategories() async {
final user = context.read<User>();
final scaffmgr = ScaffoldMessenger.of(context);
// load cached categories
final cache = await RoomCategory.list(widget.server, widget.room);
if (mounted) {
setState(() {
categories = cache;
});
}
doNetworkRequest(scaffmgr,
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(widget.server, widget.room, raw))
.toList();
setState(() {
categories = resp;
});
});
}
void fetchProducts() async {
final user = context.read<User>();
final scaffmgr = ScaffoldMessenger.of(context);
// load cached products first
final cache = await RoomProduct.list(widget.server, widget.room);
if (mounted) {
setState(() {
products = cache;
});
}
doNetworkRequest(scaffmgr,
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(widget.server, widget.room, raw))
.toList();
setState(() {
products = resp;
});
});
}
@override
void initState() {
super.initState();
// wait for background room product changes
RoomProduct.listen(widget.server, widget.room, (_) async {
try {
final updated = await RoomProduct.list(widget.server, widget.room);
setState(() {
products = updated;
});
} catch (_) {}
});
// wait for background room category changes
RoomCategory.listen(widget.server, widget.room, (_) async {
try {
final updated = await RoomCategory.list(widget.server, widget.room);
setState(() {
categories = updated;
});
} catch (_) {}
});
WidgetsBinding.instance.addPostFrameCallback((_) {
fetchCategories();
fetchProducts();
});
}
String _query = "";
void createItem(BuildContext ctx, String name, int? productID) {
final scaffMgr = ScaffoldMessenger.of(context);
final router = GoRouter.of(context);
final user = context.read<User>();
doNetworkRequest(scaffMgr,
req: () => postWithCreadentials(
target: user.server,
credentials: user,
path: 'addItem',
body: {
'room': widget.room,
'server': widget.server,
'state': 0,
'title': name,
'description': '',
'listCatID': null,
'unit': 0,
'value': '',
'listProdID': productID
}),
onOK: (body) async {
final id = body["data"]["listItemID"];
// cache item
final item = RoomItem(
id: id,
room: widget.room,
server: widget.server,
state: 0,
name: name,
description: '',
category: null,
unit: 0,
value: '',
link: productID);
await item.toDisk();
// launch edit item screen
router.pushReplacementNamed('edit-item', params: {
'server': widget.server,
'id': widget.room,
'item': id.toString()
});
});
}
void createProduct(BuildContext ctx, String name, Function(int) cb) {
final scaffMgr = ScaffoldMessenger.of(context);
final user = context.read<User>();
doNetworkRequest(scaffMgr,
req: () => postWithCreadentials(
target: user.server,
credentials: user,
path: 'addProduct',
body: {
'room': widget.room,
'server': widget.server,
'title': name,
'description': '',
'listCatID': null,
'defUnit': 0,
'defValue': '',
'ean': '',
'parent': null
}),
onOK: (body) async {
final id = body["data"]["listProdID"];
// cache product
final prod = RoomProduct(
id: id,
name: name,
server: widget.server,
room: widget.room,
description: '',
category: null,
defaultUnit: 0,
defaultValue: '',
ean: '',
parent: null);
await prod.toDisk();
cb(id);
});
}
@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: [
SearchBar(
controller: _ctrInput,
leading: const Icon(Icons.search),
hintText:
AppLocalizations.of(context)!.newItemQueryHint,
onChanged: (text) {
setState(() {
_query = text;
});
},
),
const Divider(),
// button bar
...((_query == "")
? ([
Text(AppLocalizations.of(context)!
.newItemQueryEmpty)
])
: ([
Wrap(
spacing: 20,
runSpacing: 10,
alignment: WrapAlignment.center,
crossAxisAlignment:
WrapCrossAlignment.center,
children: [
Text(AppLocalizations.of(context)!
.newItemQuickAccessPrefix),
// new item
FilledButton.icon(
onPressed: () {
// create new named item
// launch edit item screen once done
createItem(context, _query, null);
},
icon: const Icon(Icons.add),
label: Text(_query)),
// new product
FilledButton.icon(
onPressed: () {
// create new product with name,
// create new item with name
// and link to the created product
// launch edit item screen once done
createProduct(
context,
_query,
(p0) => createItem(
context, _query, p0));
},
icon: const Icon(Icons.add),
label: Text(AppLocalizations.of(
context)!
.newItemQuickProduct(_query))),
])
])),
const Divider(),
// link products search
...((products
// show all products if query is empty
// when query isn't empty show products
// that contain the query in the title
// or description
.where((element) =>
(_query == "") ||
element.name.contains(_query) ||
element.description.contains(_query))
.map((e) => ListTile(
title: Text(e.name),
subtitle: Text(e.description),
trailing: CategoryChip(
server: widget.server,
room: widget.room,
category: categories
.where((element) =>
element.id == e.category)
.firstOrNull ??
RoomCategory.other(widget.server,
widget.room, context),
),
onTap: () {
// create new item and link it to the product
// launch edit item screen once done
createItem(
context,
// use productname as item name
e.name,
e.id);
},
))))
],
))))),
);
}
}