From a608c504475e290a4dcf77b0ee18be5dc12d6ad6 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Thu, 26 Sep 2024 21:01:45 +0200 Subject: [PATCH 01/10] flow update --- lib/pages/flow/active_flow_page.dart | 2 +- lib/pages/flow/create_flow_page.dart | 46 +++++++++++++++++++--------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/pages/flow/active_flow_page.dart b/lib/pages/flow/active_flow_page.dart index d1f963e..33e9388 100644 --- a/lib/pages/flow/active_flow_page.dart +++ b/lib/pages/flow/active_flow_page.dart @@ -56,7 +56,7 @@ class _ActiveFlowPageState extends State { ), ); - if (confirm) { + if (confirm ?? false) { await API .API() .endFlow(widget.flow.id) diff --git a/lib/pages/flow/create_flow_page.dart b/lib/pages/flow/create_flow_page.dart index 2d66a66..eb9ce91 100644 --- a/lib/pages/flow/create_flow_page.dart +++ b/lib/pages/flow/create_flow_page.dart @@ -65,26 +65,40 @@ class _CreateFlowPageState extends State { }); } - Widget buildInputSelection(BuildContext context) { - return Column( - children: widget.info.depends.map((x) { - return ElevatedButton( - onPressed: () { - selectDependItems(context, x); - }, - child: Text("Add $x")); - }).toList()); + List buildInputSelection(BuildContext context) { + if (widget.info.depends.isEmpty) { + return []; + } + + return [ + Column( + children: widget.info.depends.map((x) { + return ElevatedButton( + onPressed: () { + selectDependItems(context, x); + }, + child: Text("Add $x")); + }).toList()), + const Divider(), + ]; } Widget flowContinuation() { return Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ Card( - child: Text(widget.previousFlow!.kind), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(widget.previousFlow!.kind), + ), ), - const Text(" -> "), + Card(child: Icon(Icons.arrow_right)), Card( - child: Text(widget.info.name), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(widget.info.name), + ), ) ], ); @@ -93,15 +107,17 @@ class _CreateFlowPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Create new ${widget.info.name} Flow")), + appBar: AppBar( + title: Text(widget.previousFlow != null + ? "Continue to ${widget.previousFlow!.kind}" + : "Create new ${widget.info.name} Flow")), body: Column( children: [ if (widget.previousFlow != null) ...[ flowContinuation(), const Divider() ], - buildInputSelection(context), - const Divider(), + ...buildInputSelection(context), Card( child: Column( children: depends From 2f24e0269607d359f7cae82d54e1184c75b83674 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Thu, 26 Sep 2024 21:35:28 +0200 Subject: [PATCH 02/10] sync api + ui refactor --- lib/api.dart | 84 ++++++------ lib/main.dart | 3 + lib/pages/flow/active_flow_page.dart | 21 ++- lib/pages/flow/create_flow_page.dart | 15 ++- lib/pages/flow/end_flow_page.dart | 39 +++--- lib/pages/flow/flow_info_page.dart | 13 +- lib/pages/flow/flows_page.dart | 27 ++-- lib/pages/items.dart | 18 +-- lib/pages/locations.dart | 8 +- lib/pages/setup.dart | 2 + lib/pages/supply.dart | 12 +- lib/pages/transaction.dart | 183 +++++++++++++-------------- 12 files changed, 198 insertions(+), 227 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index ae2f339..69db310 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -20,6 +20,25 @@ class API { API._internal(); + Future prefetch() async { + // todo : prefetch + // fetch items + var resp = jsonDecode(await getRequest("$instance/items")); + var lst = resp["items"] as List; + items = lst.map((x) => Item(x)).toList(); + + // fetch locations + var locResp = jsonDecode(await getRequest("$instance/locations")) + as Map; + locations = locResp.map((key, value) => MapEntry(key, Location(value))); + + // fetch flowInfos + var flowResp = + jsonDecode(await getRequest("$instance/flows")) as Map; + + flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value))); + } + Future init() async { pref = await SharedPreferences.getInstance(); instance = pref.getString("instance") ?? ""; @@ -60,14 +79,7 @@ class API { } // /items - Future> getItems() async { - if (items != null) { - return items!; - } - - var resp = jsonDecode(await getRequest("$instance/items")); - var lst = resp["items"] as List; - items = lst.map((x) => Item(x)).toList(); + List getItems() { return items!; } @@ -90,23 +102,12 @@ class API { return ret; } - Future> getLocations() async { - if (locations != null) { - return locations!; - } - - var resp = jsonDecode(await getRequest("$instance/locations")) - as Map; - locations = resp.map((key, value) => MapEntry(key, Location(value))); + Map getLocations() { return locations!; } - Future getItem(String item) async { - if (items != null) { - return items!.firstWhere((x) => x.id == item); - } - - return Item(jsonDecode(await getRequest("$instance/item/$item"))); + Item getItem(String item) { + return items!.firstWhere((x) => x.id == item); } Future getTransaction(String id) async { @@ -236,25 +237,13 @@ class API { // Flows // /flows - Future> getFlows() async { - if (flowInfos != null) { - return flowInfos!; - } - - var resp = - jsonDecode(await getRequest("$instance/flows")) as Map; - - flowInfos = resp.map((key, value) => MapEntry(key, FlowInfo(value))); + Map getFlows() { return flowInfos!; } // /flow//info - Future getFlowInfo(String id) async { - if (flowInfos != null) { - return flowInfos![id]!; - } - - return FlowInfo(jsonDecode(await getRequest("$instance/flow/$id/info"))); + FlowInfo getFlowInfo(String id) { + return flowInfos![id]!; } Future getFlow(String id) async { @@ -277,12 +266,20 @@ class API { } // /flow//end - Future?> endFlow(String id, {List? produced}) async { + Future>?> endFlow(String id, + {List? produced}) async { var resp = jsonDecode(await postRequest("$instance/flow/$id/end", {"produced": produced?.map((x) => x.json()).toList()})); + print(resp); + if (produced != null) { - return (resp["produced"] as List).cast(); + var produced = resp["produced"] as Map; + return produced.map( + (key, value) { + return MapEntry(key, (value as List).cast()); + }, + ); } return null; @@ -314,13 +311,8 @@ class API { "$instance/transaction/$id/move", {"to": newLocation})); } - Future getLocation(String id) async { - if (locations != null) { - return locations![id]!; - } - - var resp = jsonDecode(await getRequest("$instance/location/$id")); - return Location(resp); + Location getLocation(String id) { + return locations![id]!; } Future addNoteToFlow(String flowID, String content) async { diff --git a/lib/main.dart b/lib/main.dart index 6df00dd..1fb0955 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; Future main() async { await API().init(); + if (API().isInit()) { + await API().prefetch(); + } runApp(const MyApp()); } diff --git a/lib/pages/flow/active_flow_page.dart b/lib/pages/flow/active_flow_page.dart index 33e9388..12eff76 100644 --- a/lib/pages/flow/active_flow_page.dart +++ b/lib/pages/flow/active_flow_page.dart @@ -67,17 +67,16 @@ class _ActiveFlowPageState extends State { if (widget.info.next != null) IconButton( onPressed: () { - API.API().getFlowInfo(widget.info.next!).then((newInfo) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return CreateFlowPage( - newInfo, - () {}, - previousFlow: widget.flow, - ); - }, - )); - }); + var newInfo = API.API().getFlowInfo(widget.info.next!); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return CreateFlowPage( + newInfo, + () {}, + previousFlow: widget.flow, + ); + }, + )); }, icon: const Icon(Icons.arrow_forward)), ], diff --git a/lib/pages/flow/create_flow_page.dart b/lib/pages/flow/create_flow_page.dart index eb9ce91..0998424 100644 --- a/lib/pages/flow/create_flow_page.dart +++ b/lib/pages/flow/create_flow_page.dart @@ -25,11 +25,10 @@ class _CreateFlowPageState extends State { input: depends.map((x) => x.uuid).toList()) .then((x) { API.API().getFlow(x).then((flow) { - API.API().getFlowInfo(flow.kind).then((info) { - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => ActiveFlowPage(flow, info), - )); - }); + var info = API.API().getFlowInfo(flow.kind); + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => ActiveFlowPage(flow, info), + )); }); }); return; @@ -73,11 +72,13 @@ class _CreateFlowPageState extends State { return [ Column( children: widget.info.depends.map((x) { + var (item, variant) = API.itemVariant(x); return ElevatedButton( onPressed: () { selectDependItems(context, x); }, - child: Text("Add $x")); + child: + Text("Add ${API.API().getItem(item).variants[variant]!.name}")); }).toList()), const Divider(), ]; @@ -90,7 +91,7 @@ class _CreateFlowPageState extends State { Card( child: Padding( padding: const EdgeInsets.all(8.0), - child: Text(widget.previousFlow!.kind), + child: Text(API.API().getFlowInfo(widget.previousFlow!.kind).name), ), ), Card(child: Icon(Icons.arrow_right)), diff --git a/lib/pages/flow/end_flow_page.dart b/lib/pages/flow/end_flow_page.dart index 212aaef..cf4061b 100644 --- a/lib/pages/flow/end_flow_page.dart +++ b/lib/pages/flow/end_flow_page.dart @@ -23,13 +23,7 @@ class _EndFlowWithProduceState extends State { @override void initState() { super.initState(); - API.API().getLocations().then( - (value) { - setState(() { - locations = value; - }); - }, - ); + locations = API.API().getLocations(); } refresh() { @@ -46,25 +40,24 @@ class _EndFlowWithProduceState extends State { List ret = []; for (var i in widget.info.produces!) { + var (itemID, variant) = API.itemVariant(i); + var item = API.API().getItem(itemID); ret.add(ElevatedButton( onPressed: () { - var (itemID, variant) = API.itemVariant(i); - API.API().getItem(itemID).then((item) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return SupplyPage( - item, - refresh, - onlyVariants: [variant], - forcePrice: "0.00", - forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}", - onCreate: addProduced, - ); - }, - )); - }); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return SupplyPage( + item, + refresh, + onlyVariants: [variant], + forcePrice: "0.00", + forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}", + onCreate: addProduced, + ); + }, + )); }, - child: Text("Produced $i"))); + child: Text("Produced ${item.variants[variant]!.name}"))); } return ret; diff --git a/lib/pages/flow/flow_info_page.dart b/lib/pages/flow/flow_info_page.dart index aea4b78..7ba14e7 100644 --- a/lib/pages/flow/flow_info_page.dart +++ b/lib/pages/flow/flow_info_page.dart @@ -24,13 +24,17 @@ class _FlowInfoPageState extends State { body: Column( children: [ // todo : ui improve - if (widget.info.next != null) Text("Next: ${widget.info.next}"), + if (widget.info.next != null) + Text("Next: ${API.API().getFlowInfo(widget.info.next!).name}"), if (widget.info.depends.isNotEmpty) Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text("Flow can use: "), - ...widget.info.depends.map((x) => Text(x)).toList(), + ...widget.info.depends.map((x) { + var (item, variant) = API.itemVariant(x); + return Text(API.API().getItem(item).variants[variant]!.name); + }).toList(), ], ), if (widget.info.produces?.isNotEmpty ?? false) @@ -38,7 +42,10 @@ class _FlowInfoPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text("Flow can produce: "), - ...widget.info.produces!.map((x) => Text(x)).toList(), + ...widget.info.produces!.map((x) { + var (item, variant) = API.itemVariant(x); + return Text(API.API().getItem(item).variants[variant]!.name); + }).toList(), ], ), const Divider(), diff --git a/lib/pages/flow/flows_page.dart b/lib/pages/flow/flows_page.dart index 8bcac95..5bfaf35 100644 --- a/lib/pages/flow/flows_page.dart +++ b/lib/pages/flow/flows_page.dart @@ -20,13 +20,7 @@ class _FlowsPageState extends State { @override void initState() { super.initState(); - API.API().getFlows().then( - (value) { - setState(() { - flowInfos = value; - }); - }, - ); + flowInfos = API.API().getFlows(); } Widget flowTile(BuildContext context, API.FlowInfo x) { @@ -94,7 +88,8 @@ class _FlowsPageState extends State { children: producedMapping[key]!.map((x) { return flowTile(context, x); }).toList()); - items.add(ExpandableListItem(body: flows, header: Text(key))); + items.add(ExpandableListItem( + body: flows, header: Text(API.API().getItem(key).name))); } return ExpandableList(items); @@ -122,7 +117,8 @@ class _FlowsPageState extends State { children: dependsMapping[key]!.map((x) { return flowTile(context, x); }).toList()); - items.add(ExpandableListItem(body: flows, header: Text(key))); + items.add(ExpandableListItem( + body: flows, header: Text(API.API().getItem(key).name))); } return ExpandableList(items); @@ -169,13 +165,12 @@ class _FlowsPageState extends State { code = code!.replaceFirst("Code scanned = ", ""); API.API().getFlow(code).then((flow) { - API.API().getFlowInfo(flow.kind).then((info) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return ActiveFlowPage(flow, info); - }, - )); - }); + var info = API.API().getFlowInfo(flow.kind); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return ActiveFlowPage(flow, info); + }, + )); }); }, ); diff --git a/lib/pages/items.dart b/lib/pages/items.dart index fa5db2f..70304e5 100644 --- a/lib/pages/items.dart +++ b/lib/pages/items.dart @@ -13,20 +13,10 @@ class ItemsPage extends StatelessWidget { appBar: AppBar( title: const Text("Items"), ), - body: FutureBuilder( - future: API().getItems(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const CircularProgressIndicator(); - } - - var items = snapshot.data!; - - return ListView( - children: items.map((x) { - return ItemTile(x); - }).toList()); - }), + body: ListView( + children: API().getItems().map((x) { + return ItemTile(x); + }).toList()), floatingActionButton: FloatingActionButton( onPressed: () { // scan transaction code diff --git a/lib/pages/locations.dart b/lib/pages/locations.dart index 7d00bd9..ca12c23 100644 --- a/lib/pages/locations.dart +++ b/lib/pages/locations.dart @@ -17,13 +17,7 @@ class _LocationsPageState extends State { @override void initState() { super.initState(); - API().getLocations().then( - (value) { - setState(() { - locations = value; - }); - }, - ); + locations = API().getLocations(); } TreeNode buildTree(BuildContext context, String locID) { diff --git a/lib/pages/setup.dart b/lib/pages/setup.dart index 620413d..667861d 100644 --- a/lib/pages/setup.dart +++ b/lib/pages/setup.dart @@ -31,6 +31,8 @@ class _SetupPageState extends State { const SnackBar(content: Text('Setup Complete!')), ); + await API().prefetch(); + // Navigate or close the setup screen Navigator.pushReplacement( context, diff --git a/lib/pages/supply.dart b/lib/pages/supply.dart index 342a352..18c3bb3 100644 --- a/lib/pages/supply.dart +++ b/lib/pages/supply.dart @@ -126,7 +126,7 @@ class _SupplyPageState extends State { const SizedBox(height: 16), // Origin Field with Dropdown and Text Input - if (widget.forceOrigin == null) + if (widget.forceOrigin == null) ...[ AutocompletedTextField( options: origins, getValue: () => _selectedOrigin, @@ -146,11 +146,11 @@ class _SupplyPageState extends State { }); }, label: "Origin"), - - const SizedBox(height: 16), + const SizedBox(height: 16), + ], // Price Field - if (widget.forcePrice == null) + if (widget.forcePrice == null) ...[ TextFormField( decoration: const InputDecoration(labelText: 'Price'), keyboardType: TextInputType.number, @@ -165,8 +165,8 @@ class _SupplyPageState extends State { return null; }, ), - - const SizedBox(height: 16), + const SizedBox(height: 16), + ], // Location Dropdown Row( diff --git a/lib/pages/transaction.dart b/lib/pages/transaction.dart index a7d2d62..1de2095 100644 --- a/lib/pages/transaction.dart +++ b/lib/pages/transaction.dart @@ -40,84 +40,80 @@ class _TransactionPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.transaction.item), + title: Text(API().getItem(transaction.item).name), actions: [ IconButton( onPressed: () { - API().getLocations().then((locations) { - List locationList = locations.keys.toList(); - String? selectedLocationID; + var locations = API().getLocations(); + List locationList = locations.keys.toList(); + String? selectedLocationID; - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Move Transaction'), - content: Row( - mainAxisSize: MainAxisSize.min, - children: [ - DropdownButton( - value: selectedLocationID, - onChanged: (value) { - selectedLocationID = value!; - API() - .moveTransaction(widget.transaction.uuid, - selectedLocationID!) - .then((x) { - Navigator.of(context).pop(); - }); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Move Transaction'), + content: Row( + mainAxisSize: MainAxisSize.min, + children: [ + DropdownButton( + value: selectedLocationID, + onChanged: (value) { + selectedLocationID = value!; + API() + .moveTransaction(widget.transaction.uuid, + selectedLocationID!) + .then((x) { + Navigator.of(context).pop(); + }); + setState(() {}); + }, + items: locationList + .map>((locationID) { + return DropdownMenuItem( + value: locationID, + child: Text(locations[locationID]!.name), + ); + }).toList(), + ), + IconButton( + onPressed: () { + var locations = API().getLocations(); + QrBarCodeScannerDialog().getScannedQrBarCode( + context: context, + onCode: (code) { + // library is retarded + code = code! + .replaceFirst("Code scanned = ", ""); + if (!locations.containsKey(code)) { + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + 'The location $code does not exist.')), + ); + return; + } + + API() + .moveTransaction( + widget.transaction.uuid, + selectedLocationID!) + .then( + (x) { + Navigator.of(context).pop(); + }, + ); + }); setState(() {}); }, - items: locationList - .map>((locationID) { - return DropdownMenuItem( - value: locationID, - child: Text(locations[locationID]!.name), - ); - }).toList(), - ), - IconButton( - onPressed: () { - API().getLocations().then((locations) { - QrBarCodeScannerDialog() - .getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst( - "Code scanned = ", ""); - if (!locations - .containsKey(code)) { - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text( - 'The location $code does not exist.')), - ); - return; - } - - API() - .moveTransaction( - widget.transaction.uuid, - selectedLocationID!) - .then( - (x) { - Navigator.of(context).pop(); - }, - ); - }); - }); - setState(() {}); - }, - icon: const Icon(Icons.qr_code)) - ], - ), - ); - }, - ); - }); + icon: const Icon(Icons.qr_code)) + ], + ), + ); + }, + ); }, icon: const Icon(Icons.move_up)) ], @@ -143,12 +139,12 @@ class _TransactionPageState extends State { children: [ // todo : human names Text( - "${transaction.item}", + "${API().getItem(transaction.item).name}", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 28), ), Text( - "${transaction.variant}", + "${API().getItem(transaction.item).variants[transaction.variant]!.name}", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, @@ -262,14 +258,14 @@ class TransactionCard extends StatelessWidget { Row( children: [ Text( - t.item, + API().getItem(t.item).name, style: const TextStyle(fontSize: 16), ), const SizedBox( - width: 4, + width: 8, ), Text( - t.variant, + API().getItem(t.item).variants[t.variant]!.name, style: TextStyle(fontSize: 14, color: Colors.grey[400]), ), ], @@ -358,29 +354,28 @@ class TransactionSelectPage extends StatelessWidget { selectionList.add(s); } - if (selectionList.isEmpty) { - Navigator.of(context).pop(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('No Transactions to select')), - ); - } - return Scaffold( appBar: AppBar( title: const Text("Select a Transaction"), ), body: ListView( - children: selectionList - .map((x) => TransactionCard( - x, - () {}, - onLongPress: (x) {}, - onTap: (t) { - onSelect(t); - Navigator.of(context).pop(); - }, - )) - .toList()), + children: selectionList.isEmpty + ? [ + ListTile( + title: Center(child: Text("No Transactions available")), + ) + ] + : selectionList + .map((x) => TransactionCard( + x, + () {}, + onLongPress: (x) {}, + onTap: (t) { + onSelect(t); + Navigator.of(context).pop(); + }, + )) + .toList()), ); } } From 167a4620fe55edbbcd281b6fabde7f6e5d151ab2 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Fri, 27 Sep 2024 09:27:29 +0200 Subject: [PATCH 03/10] api --- lib/api.dart | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/api.dart b/lib/api.dart index 69db310..e42b99d 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -198,7 +198,13 @@ class API { } // /item///stat - Future getStat(String item, String variant) async { + Future getStat(String item, String variant, + {bool full = false}) async { + if (full) { + return FullItemVariantStat(jsonDecode( + await getRequest("$instance/item/$item/$variant/stat?full=true"))); + } + return ItemVariantStat( jsonDecode(await getRequest("$instance/item/$item/$variant/stat"))); } @@ -570,3 +576,28 @@ class GlobalItemStat { var split = iv.split("::"); return (split[0], split[1]); } + +class FullItemVariantStat { + late int amount; + late double totalPrice; + late double expiryRate; + late Map origins; + + FullItemVariantStat(Map json) { + amount = json["amount"]; + totalPrice = json["total_price"]; + expiryRate = json["expiry_rate"]; + origins = (json["origins"] as Map) + .map((key, value) => MapEntry(key, OriginStat(value))); + } +} + +class OriginStat { + late double average_price; + late int inventory; + + OriginStat(Map json) { + average_price = json["average_price"]; + inventory = json["inventory"]; + } +} From d21554cddcf9e6c0c0f81ed9d463bf298edd6c46 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Fri, 27 Sep 2024 13:15:53 +0200 Subject: [PATCH 04/10] update stat --- lib/pages/stats.dart | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/pages/stats.dart b/lib/pages/stats.dart index b612280..e50b52e 100644 --- a/lib/pages/stats.dart +++ b/lib/pages/stats.dart @@ -1,4 +1,5 @@ import 'package:cdb_ui/api.dart'; +import 'package:cdb_ui/pages/expandable_list.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; @@ -85,16 +86,48 @@ class ItemStatPage extends StatelessWidget { const ItemStatPage(this.item, {super.key}); - // todo : expiry ratio // todo : avg time of transaction active + ExpandableListItem buildVariantStat(String variant) { + return ExpandableListItem( + body: FutureBuilder( + future: API().getStat(item.id, variant, full: true), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const CircularProgressIndicator(); + } + + var data = snapshot.data! as FullItemVariantStat; + + return Column( + children: [ + Text("Amount: ${data.amount}"), + Text("Total Cost: ${data.totalPrice}"), + Text("Expiry Rate: ${data.expiryRate}"), + ...data.origins.keys.map((key) { + var originStat = data.origins[key]!; + return Column(children: [ + Text("Inventory: ${originStat.inventory}"), + Text( + "Average Price: ${originStat.average_price.toStringAsFixed(2)} €"), + ]); + }).toList() + ], + ); + }, + ), + header: Text(item.variants[variant]!.name)); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Statistics of ${item.name}"), ), - body: null, + body: Column(children: [ + ExpandableList(item.variants.keys.map(buildVariantStat).toList()) + ]), ); } } From b13b385cb75b5a2336a86f3facb5cf0534d40612 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Mon, 7 Oct 2024 21:48:48 +0200 Subject: [PATCH 05/10] update --- android/app/build.gradle | 3 +- android/app/src/main/AndroidManifest.xml | 3 +- lib/api.dart | 63 ++++++++++-------------- lib/main.dart | 48 +++++++++++++----- lib/pages/flow/end_flow_page.dart | 2 - lib/pages/stats.dart | 1 + lib/pages/supply.dart | 15 ++++-- lib/pages/transaction.dart | 9 ++-- pubspec.lock | 8 +-- pubspec.yaml | 2 +- 10 files changed, 84 insertions(+), 70 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 29d8a3d..9c1dbee 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,8 +41,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.cdb_ui" + applicationId "de.hydrar.red.cdb" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion flutter.minSdkVersion diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cf5af51..b1ca99c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + init() async { pref = await SharedPreferences.getInstance(); - instance = pref.getString("instance") ?? ""; + instance = pref!.getString("instance") ?? ""; } bool isInit() { - return pref.containsKey("token") && pref.containsKey("instance"); + if (pref == null) { + return false; + } + + return pref!.containsKey("token") && pref!.containsKey("instance"); + } + + bool isPrefetched() { + return items != null && locations != null && flowInfos != null; } void save(String instance, String token) { - pref.setString("instance", instance); - pref.setString("token", token); + pref!.setString("instance", instance); + pref!.setString("token", token); this.instance = instance; } @@ -60,7 +68,7 @@ class API { var resp = await http.get(Uri.parse(url), headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json; charset=UTF-8', - 'Token': pref.getString("token")! + 'Token': pref!.getString("token")! }); return utf8.decode(resp.bodyBytes); @@ -71,7 +79,7 @@ class API { headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json; charset=UTF-8', - 'Token': pref.getString("token")! + 'Token': pref!.getString("token")! }, body: jsonEncode(data)); @@ -163,7 +171,7 @@ class API { } // /supply - Future supplyItem(String item, String variant, String price, + Future supplyItem(String item, String variant, double price, String? origin, String? location, String? note) async { if (origin!.isEmpty) { origin = null; @@ -214,7 +222,7 @@ class API { } // /item///price_history? - Future> getPriceHistory(String item, String variant, + Future> getPriceHistory(String item, String variant, {String? origin}) async { var url = "$instance/item/$item/$variant/price_history"; @@ -224,10 +232,10 @@ class API { var resp = jsonDecode(await getRequest(url)) as List; - return resp.map((x) => Price(x)).toList(); + return resp.map((x) => x as double).toList(); } - Future getLatestPrice(String item, String variant, + Future getLatestPrice(String item, String variant, {String? origin}) async { var url = "$instance/item/$item/$variant/price_latest"; @@ -237,7 +245,7 @@ class API { var resp = jsonDecode(await getRequest(url)) as Map; - return Price(resp); + return resp as double; } // Flows @@ -386,31 +394,11 @@ class ItemVariant { } } -class Price { - late double value; - late String currency; - - Price(Map json) { - value = json["value"]; - currency = json["currency"]; - } - - Price.fromString(String value) { - var priceSplit = value.split(" "); - this.value = double.parse(priceSplit[0]); - currency = priceSplit[1]; - } - - String format() { - return "${value.toStringAsFixed(2)} $currency"; - } -} - class Transaction { late String uuid; late String item; late String variant; - late Price price; + late double price; String? origin; late int timestamp; ConsumeInfo? consumed; @@ -422,7 +410,7 @@ class Transaction { uuid = json["uuid"]; item = json["item"]; variant = json["variant"]; - price = Price(json["price"]); + price = json["price"]; origin = json["origin"]; timestamp = json["timestamp"]; expired = json["expired"]; @@ -431,12 +419,11 @@ class Transaction { location = json["location"] != null ? Location(json["location"]) : null; } - Transaction.inMemory(String itemID, String variantID, String price, + Transaction.inMemory(String itemID, String variantID, this.price, String? origin, Location? location, String? note) { uuid = ""; item = itemID; variant = variantID; - this.price = Price.fromString(price); origin = origin; timestamp = 0; consumed = null; @@ -448,12 +435,12 @@ class Transaction { class ConsumeInfo { late String destination; - late Price price; + late double price; late int timestamp; ConsumeInfo(Map json) { destination = json["destination"]; - price = Price(json["price"]); + price = json["price"]; timestamp = json["timestamp"]; } } diff --git a/lib/main.dart b/lib/main.dart index 1fb0955..a194491 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,27 +7,49 @@ import 'package:cdb_ui/pages/stats.dart'; import 'package:flutter/material.dart'; Future main() async { - await API().init(); - if (API().isInit()) { - await API().prefetch(); - } runApp(const MyApp()); } -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + bool init = false; + + @override + void initState() { + super.initState(); + () async { + await API().init(); + if (API().isInit()) { + await API().prefetch(); + setState(() { + init = true; + }); + } + }(); + } + @override Widget build(BuildContext context) { return MaterialApp( - title: 'CDB', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.deepPurple, brightness: Brightness.dark), - useMaterial3: true, - ), - home: API().isInit() ? const MyHomePage() : const SetupPage(), - ); + title: 'CDB', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.deepPurple, brightness: Brightness.dark), + useMaterial3: true, + ), + home: API().isInit() + ? (API().isPrefetched() + ? const MyHomePage() + : const Scaffold( + body: CircularProgressIndicator(), + )) + : const SetupPage()); } } diff --git a/lib/pages/flow/end_flow_page.dart b/lib/pages/flow/end_flow_page.dart index cf4061b..32f233d 100644 --- a/lib/pages/flow/end_flow_page.dart +++ b/lib/pages/flow/end_flow_page.dart @@ -1,5 +1,3 @@ -import 'dart:js_interop_unsafe'; - import 'package:cdb_ui/api.dart' as API; import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/supply.dart'; diff --git a/lib/pages/stats.dart b/lib/pages/stats.dart index e50b52e..91ddd66 100644 --- a/lib/pages/stats.dart +++ b/lib/pages/stats.dart @@ -24,6 +24,7 @@ class StatsPage extends StatelessWidget { // global origin / destinations return Scaffold( + appBar: AppBar(title: Text("Home"),), body: FutureBuilder( future: _fetchData(), builder: (context, snapshot) { diff --git a/lib/pages/supply.dart b/lib/pages/supply.dart index 18c3bb3..bb3dad3 100644 --- a/lib/pages/supply.dart +++ b/lib/pages/supply.dart @@ -53,7 +53,7 @@ class _SupplyPageState extends State { var t = SupplyForm( itemID: widget.item.id, variant: variant, - price: "${_priceController.text} €", + price: double.parse(_priceController.text), origin: _selectedOrigin, location: _selectedLocation, note: _noteController.text); @@ -66,8 +66,13 @@ class _SupplyPageState extends State { } API() - .supplyItem(widget.item.id, variant, "${_priceController.text} €", - _selectedOrigin, _selectedLocation, _noteController.text) + .supplyItem( + widget.item.id, + variant, + double.parse(_priceController.text), + _selectedOrigin, + _selectedLocation, + _noteController.text) .then((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item added successfully!')), @@ -138,7 +143,7 @@ class _SupplyPageState extends State { ? await API() .getLatestPrice(widget.item.id, variant, origin: selection) - .then((x) => x.value.toStringAsFixed(2)) + .then((x) => x.toStringAsFixed(2)) : _priceController.text; setState(() { _priceController.text = price; @@ -293,7 +298,7 @@ class AutocompletedTextField extends StatelessWidget { class SupplyForm { final String itemID; final String variant; - final String price; + final double price; final String? origin; final String? location; final String note; diff --git a/lib/pages/transaction.dart b/lib/pages/transaction.dart index 1de2095..7faee85 100644 --- a/lib/pages/transaction.dart +++ b/lib/pages/transaction.dart @@ -165,7 +165,7 @@ class _TransactionPageState extends State { if (transaction.expired) const Text("Transaction is Expired!"), - IconText(Icons.money, transaction.price.format(), + IconText(Icons.money, transaction.price.toStringAsFixed(2), color: Colors.green), if (transaction.origin != null) @@ -181,7 +181,8 @@ class _TransactionPageState extends State { Text("Consumed on: ${tsFormat(transaction.consumed!.timestamp)}"), IconText(Icons.store, transaction.consumed!.destination, color: Colors.blue), - IconText(Icons.money, transaction.consumed!.price.format(), + IconText( + Icons.money, transaction.consumed!.price.toStringAsFixed(2), color: Colors.green), ] @@ -286,8 +287,7 @@ class TransactionCard extends StatelessWidget { const SizedBox( height: 10, ), - IconText(Icons.money, - "${t.price.value.toStringAsFixed(2)} ${t.price.currency}", + IconText(Icons.money, "${t.price.toStringAsFixed(2)} €", color: Colors.green), if (t.origin != null) ...[ const SizedBox(height: 8), @@ -328,6 +328,7 @@ class IconText extends StatelessWidget { Text( text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + overflow: TextOverflow.fade, ), ], ); diff --git a/pubspec.lock b/pubspec.lock index 8ec77fd..8626fab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -124,10 +124,10 @@ packages: dependency: "direct main" description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" http_parser: dependency: transitive description: @@ -417,10 +417,10 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c19fe54..7370483 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: cdb_ui -description: A new Flutter project. +description: Economic Database. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev From fcecb5aad5a8c8b82c87df19051ba82f030d4775 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Tue, 8 Oct 2024 10:11:18 +0200 Subject: [PATCH 06/10] update --- lib/api.dart | 3 +- lib/main.dart | 8 +- lib/pages/flow/flows_page.dart | 29 ++-- lib/pages/items.dart | 22 ++- lib/pages/locations.dart | 31 ++--- lib/pages/supply.dart | 35 +++-- lib/pages/transaction.dart | 47 +++---- pubspec.lock | 130 +++++++++++++----- pubspec.yaml | 2 +- .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 11 files changed, 186 insertions(+), 129 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index ee016ee..3f6f4fd 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -39,9 +39,10 @@ class API { flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value))); } - Future init() async { + Future init(Function refresh) async { pref = await SharedPreferences.getInstance(); instance = pref!.getString("instance") ?? ""; + refresh(); } bool isInit() { diff --git a/lib/main.dart b/lib/main.dart index a194491..815427e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,11 +20,15 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { bool init = false; + refresh() { + setState(() {}); + } + @override void initState() { super.initState(); () async { - await API().init(); + await API().init(refresh); if (API().isInit()) { await API().prefetch(); setState(() { @@ -47,7 +51,7 @@ class _MyAppState extends State { ? (API().isPrefetched() ? const MyHomePage() : const Scaffold( - body: CircularProgressIndicator(), + body: Center(child: CircularProgressIndicator()), )) : const SetupPage()); } diff --git a/lib/pages/flow/flows_page.dart b/lib/pages/flow/flows_page.dart index 5bfaf35..f172bea 100644 --- a/lib/pages/flow/flows_page.dart +++ b/lib/pages/flow/flows_page.dart @@ -2,9 +2,8 @@ import 'package:cdb_ui/api.dart' as API; import 'package:cdb_ui/pages/expandable_list.dart'; import 'package:cdb_ui/pages/flow/active_flow_page.dart'; import 'package:cdb_ui/pages/flow/flow_info_page.dart'; -import 'package:cdb_ui/pages/transaction.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:flutter/material.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class FlowsPage extends StatefulWidget { const FlowsPage({super.key}); @@ -156,24 +155,18 @@ class _FlowsPageState extends State { _ => const Text("..."), }, floatingActionButton: FloatingActionButton( - onPressed: () { + onPressed: () async { // scan flow code - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst("Code scanned = ", ""); + var code = await scanQRCode(context, title: "Scan Flow Code"); - API.API().getFlow(code).then((flow) { - var info = API.API().getFlowInfo(flow.kind); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return ActiveFlowPage(flow, info); - }, - )); - }); - }, - ); + API.API().getFlow(code!).then((flow) { + var info = API.API().getFlowInfo(flow.kind); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return ActiveFlowPage(flow, info); + }, + )); + }); }, child: const Icon(Icons.qr_code), ), diff --git a/lib/pages/items.dart b/lib/pages/items.dart index 70304e5..71fc15c 100644 --- a/lib/pages/items.dart +++ b/lib/pages/items.dart @@ -1,8 +1,8 @@ import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/itemview.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class ItemsPage extends StatelessWidget { const ItemsPage({super.key}); @@ -18,21 +18,15 @@ class ItemsPage extends StatelessWidget { return ItemTile(x); }).toList()), floatingActionButton: FloatingActionButton( - onPressed: () { + onPressed: () async { // scan transaction code - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst("Code scanned = ", ""); + var code = await scanQRCode(context, title: "Scan Transaction Code"); - API().getTransaction(code).then((t) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => TransactionPage(t), - )); - }); - }, - ); + API().getTransaction(code!).then((t) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => TransactionPage(t), + )); + }); }, child: const Icon(Icons.qr_code), ), diff --git a/lib/pages/locations.dart b/lib/pages/locations.dart index ca12c23..5ddb754 100644 --- a/lib/pages/locations.dart +++ b/lib/pages/locations.dart @@ -1,8 +1,8 @@ import 'package:cdb_ui/api.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class LocationsPage extends StatefulWidget { const LocationsPage({super.key}); @@ -63,25 +63,20 @@ class _LocationsPageState extends State { return buildTree(context, key); }).toList()), floatingActionButton: FloatingActionButton( - onPressed: () { + onPressed: () async { // scan location code - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code!.replaceFirst("Code scanned = ", ""); - if (!locations!.containsKey(code)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('The location $code does not exist.')), - ); - return; - } + var code = await scanQRCode(context, title: "Scan Location Code"); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => LocationView(locations![code]!), - )); - }, - ); + if (!locations!.containsKey(code)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('The location $code does not exist.')), + ); + return; + } + + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => LocationView(locations![code]!), + )); }, child: const Icon(Icons.qr_code), ), diff --git a/lib/pages/supply.dart b/lib/pages/supply.dart index bb3dad3..792b766 100644 --- a/lib/pages/supply.dart +++ b/lib/pages/supply.dart @@ -1,6 +1,21 @@ import 'package:cdb_ui/api.dart'; import 'package:flutter/material.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; +import 'package:simple_barcode_scanner/enum.dart'; +import 'package:simple_barcode_scanner/simple_barcode_scanner.dart'; + +Future scanQRCode(BuildContext context, + {String title = "Scan QR Code"}) async { + var res = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SimpleBarcodeScannerPage( + scanType: ScanType.qr, + appBarTitle: title, + ), + )); + + return res; +} class SupplyPage extends StatefulWidget { final Item item; @@ -202,15 +217,13 @@ class _SupplyPageState extends State { width: 12, ), IconButton( - onPressed: () { - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - setState(() { - _selectedLocation = code!; - }); - }, - ); + onPressed: () async { + var code = await scanQRCode(context); + setState(() { + if (API().getLocations().keys.contains(code)) { + _selectedLocation = code!; + } + }); }, icon: const Icon(Icons.qr_code), ), @@ -240,7 +253,7 @@ class _SupplyPageState extends State { } Future> _fetchData() async { - var locations = await API().getLocations(); + var locations = API().getLocations(); var origins = await API().getUniqueField(widget.item.id, variant, "origin"); origins.insert(0, ""); locations[""] = Location.zero(); diff --git a/lib/pages/transaction.dart b/lib/pages/transaction.dart index 7faee85..acad5dc 100644 --- a/lib/pages/transaction.dart +++ b/lib/pages/transaction.dart @@ -1,9 +1,9 @@ import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/consume.dart'; +import 'package:cdb_ui/pages/supply.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:qr_bar_code/qr/qr.dart'; -import 'package:qr_bar_code_scanner_dialog/qr_bar_code_scanner_dialog.dart'; class TransactionPage extends StatefulWidget { final Transaction transaction; @@ -78,34 +78,27 @@ class _TransactionPageState extends State { }).toList(), ), IconButton( - onPressed: () { + onPressed: () async { var locations = API().getLocations(); - QrBarCodeScannerDialog().getScannedQrBarCode( - context: context, - onCode: (code) { - // library is retarded - code = code! - .replaceFirst("Code scanned = ", ""); - if (!locations.containsKey(code)) { - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text( - 'The location $code does not exist.')), - ); - return; - } + var code = await scanQRCode(context, + title: "Scan Location Code"); + if (!locations.containsKey(code)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'The location $code does not exist.')), + ); + return; + } - API() - .moveTransaction( - widget.transaction.uuid, - selectedLocationID!) - .then( - (x) { - Navigator.of(context).pop(); - }, - ); - }); + API() + .moveTransaction(widget.transaction.uuid, + selectedLocationID!) + .then( + (x) { + Navigator.of(context).pop(); + }, + ); setState(() {}); }, icon: const Icon(Icons.qr_code)) diff --git a/pubspec.lock b/pubspec.lock index 8626fab..92bae6e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,6 +94,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_barcode_scanner: + dependency: transitive + description: + name: flutter_barcode_scanner + sha256: a4ba37daf9933f451a5e812c753ddd045d6354e4a3280342d895b07fecaab3fa + url: "https://pub.dev" + source: hosted + version: "2.0.0" flutter_lints: dependency: "direct dev" description: @@ -102,6 +110,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda" + url: "https://pub.dev" + source: hosted + version: "2.0.22" flutter_simple_treeview: dependency: "direct main" description: @@ -144,30 +160,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -196,18 +204,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -240,6 +248,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: transitive + description: + name: permission_handler + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" + url: "https://pub.dev" + source: hosted + version: "11.3.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa" + url: "https://pub.dev" + source: hosted + version: "12.0.12" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 + url: "https://pub.dev" + source: hosted + version: "9.4.5" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 + url: "https://pub.dev" + source: hosted + version: "0.1.3+2" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 + url: "https://pub.dev" + source: hosted + version: "4.2.3" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" platform: dependency: transitive description: @@ -264,22 +320,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - qr_bar_code_scanner_dialog: - dependency: "direct main" - description: - name: qr_bar_code_scanner_dialog - sha256: dcb937816d4e562141530265bd1ca39fe00f57000fd79e26c163c957d443e9e4 - url: "https://pub.dev" - source: hosted - version: "0.0.5" - qr_code_scanner: - dependency: transitive - description: - name: qr_code_scanner - sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd - url: "https://pub.dev" - source: hosted - version: "1.0.1" shared_preferences: dependency: "direct main" description: @@ -336,6 +376,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + simple_barcode_scanner: + dependency: "direct main" + description: + name: simple_barcode_scanner + sha256: "52b30082ebd6fab1e6314cb9bfc1aca5372890616dcb89d0e254edf7b7ef4951" + url: "https://pub.dev" + source: hosted + version: "0.1.2" sky_engine: dependency: transitive description: flutter @@ -385,10 +433,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -409,10 +457,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" web: dependency: transitive description: @@ -421,6 +469,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + webview_windows: + dependency: transitive + description: + name: webview_windows + sha256: "47fcad5875a45db29dbb5c9e6709bf5c88dcc429049872701343f91ed7255730" + url: "https://pub.dev" + source: hosted + version: "0.4.0" xdg_directories: dependency: transitive description: @@ -430,5 +486,5 @@ packages: source: hosted version: "1.0.4" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.5.3 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7370483..461ce7e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,7 +32,7 @@ dependencies: sdk: flutter flutter_simple_treeview: ^3.0.2 qr_bar_code: ^1.3.0 - qr_bar_code_scanner_dialog: ^0.0.5 + simple_barcode_scanner: ^0.1.2 intl: ^0.18.0 shared_preferences: ^2.1.0 fl_chart: ^0.69.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index e8bdf58..966ec1a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,15 @@ #include "generated_plugin_registrant.h" +#include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); QrBarCodePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("QrBarCodePluginCApi")); + WebviewWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WebviewWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 6108823..038df8f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,7 +3,9 @@ # list(APPEND FLUTTER_PLUGIN_LIST + permission_handler_windows qr_bar_code + webview_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 0e2ccb9e54ccf585fe06dd6b5879aa9dc7dc8181 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Tue, 8 Oct 2024 10:55:30 +0200 Subject: [PATCH 07/10] add barcode quick add --- lib/api.dart | 22 +++++++++++---------- lib/pages/flow/active_flow_page.dart | 2 +- lib/pages/flow/create_flow_page.dart | 2 +- lib/pages/items.dart | 29 ++++++++++++++++++++++++++++ lib/pages/itemview.dart | 3 +-- lib/pages/locations.dart | 2 +- lib/pages/stats.dart | 12 +++++++----- lib/pages/transaction.dart | 9 ++++++--- 8 files changed, 58 insertions(+), 23 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index 3f6f4fd..1ae5dc6 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -286,8 +286,6 @@ class API { var resp = jsonDecode(await postRequest("$instance/flow/$id/end", {"produced": produced?.map((x) => x.json()).toList()})); - print(resp); - if (produced != null) { var produced = resp["produced"] as Map; return produced.map( @@ -385,6 +383,7 @@ class ItemVariant { late String name; int? min; int? expiry; + List? barcodes; ItemVariant(Map json) { item = json["item"]; @@ -392,6 +391,9 @@ class ItemVariant { name = json["name"]; min = json["min"]; expiry = json["expiry"]; + barcodes = json["barcodes"] != null + ? (json["barcodes"] as List).cast() + : null; } } @@ -549,14 +551,14 @@ class FlowNote { } class GlobalItemStat { - late int item_count; - late int total_transactions; - late double total_price; + late int itemCount; + late int totalTransactions; + late double totalPrice; GlobalItemStat(Map json) { - item_count = json["item_count"]; - total_transactions = json["total_transactions"]; - total_price = json["total_price"]; + itemCount = json["item_count"]; + totalTransactions = json["total_transactions"]; + totalPrice = json["total_price"]; } } @@ -581,11 +583,11 @@ class FullItemVariantStat { } class OriginStat { - late double average_price; + late double averagePrice; late int inventory; OriginStat(Map json) { - average_price = json["average_price"]; + averagePrice = json["average_price"]; inventory = json["inventory"]; } } diff --git a/lib/pages/flow/active_flow_page.dart b/lib/pages/flow/active_flow_page.dart index 12eff76..d43b74e 100644 --- a/lib/pages/flow/active_flow_page.dart +++ b/lib/pages/flow/active_flow_page.dart @@ -82,7 +82,7 @@ class _ActiveFlowPageState extends State { ], ), body: Padding( - padding: EdgeInsets.symmetric(horizontal: 18.0), + padding: const EdgeInsets.symmetric(horizontal: 18.0), child: Column( children: [ Row( diff --git a/lib/pages/flow/create_flow_page.dart b/lib/pages/flow/create_flow_page.dart index 0998424..35d5fb2 100644 --- a/lib/pages/flow/create_flow_page.dart +++ b/lib/pages/flow/create_flow_page.dart @@ -94,7 +94,7 @@ class _CreateFlowPageState extends State { child: Text(API.API().getFlowInfo(widget.previousFlow!.kind).name), ), ), - Card(child: Icon(Icons.arrow_right)), + const Card(child: Icon(Icons.arrow_right)), Card( child: Padding( padding: const EdgeInsets.all(8.0), diff --git a/lib/pages/items.dart b/lib/pages/items.dart index 71fc15c..b4daa87 100644 --- a/lib/pages/items.dart +++ b/lib/pages/items.dart @@ -7,11 +7,40 @@ import 'package:flutter/material.dart'; class ItemsPage extends StatelessWidget { const ItemsPage({super.key}); + _scanBarcodeSupply(BuildContext context) async { + var code = + int.parse(await scanQRCode(context, title: "Scan Barcode") ?? "0"); + + var items = API().getItems(); + for (var item in items) { + for (var variant in item.variants.keys) { + for (var barcode in item.variants[variant]!.barcodes ?? []) { + if (code == barcode) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => SupplyPage( + item, + () {}, + onlyVariants: [variant], + ))); + } + } + } + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Items"), + // todo : add barcode scan + actions: [ + ElevatedButton( + onPressed: () { + _scanBarcodeSupply(context); + }, + child: const Icon(Icons.add_box)) + ], ), body: ListView( children: API().getItems().map((x) { diff --git a/lib/pages/itemview.dart b/lib/pages/itemview.dart index ab9e418..a6e7363 100644 --- a/lib/pages/itemview.dart +++ b/lib/pages/itemview.dart @@ -2,7 +2,6 @@ import 'package:cdb_ui/api.dart'; import 'package:cdb_ui/pages/stats.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'supply.dart'; // todo : show est. time remaining until inventory gets empty (based on demand) @@ -33,7 +32,7 @@ class _ItemViewState extends State { builder: (context) => ItemStatPage(widget.item), )); }, - icon: Icon(Icons.bar_chart)) + icon: const Icon(Icons.bar_chart)) ], ), body: Column(children: [ diff --git a/lib/pages/locations.dart b/lib/pages/locations.dart index 5ddb754..5e62a79 100644 --- a/lib/pages/locations.dart +++ b/lib/pages/locations.dart @@ -107,7 +107,7 @@ class _LocationViewState extends State { title: Text(widget.location.name), ), body: Padding( - padding: EdgeInsets.symmetric(horizontal: 10.0), + padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( children: [ Card( diff --git a/lib/pages/stats.dart b/lib/pages/stats.dart index 91ddd66..cb99e2a 100644 --- a/lib/pages/stats.dart +++ b/lib/pages/stats.dart @@ -24,7 +24,9 @@ class StatsPage extends StatelessWidget { // global origin / destinations return Scaffold( - appBar: AppBar(title: Text("Home"),), + appBar: AppBar( + title: const Text("Home"), + ), body: FutureBuilder( future: _fetchData(), builder: (context, snapshot) { @@ -47,11 +49,11 @@ class StatsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconText(Icons.data_object, - "Items: ${globalStat.item_count}"), + "Items: ${globalStat.itemCount}"), IconText(Icons.list_alt, - "Inventory: ${globalStat.total_transactions}"), + "Inventory: ${globalStat.totalTransactions}"), IconText(Icons.money, - "Price: ${globalStat.total_price.toStringAsFixed(2)} €") + "Price: ${globalStat.totalPrice.toStringAsFixed(2)} €") ], ), )), @@ -110,7 +112,7 @@ class ItemStatPage extends StatelessWidget { return Column(children: [ Text("Inventory: ${originStat.inventory}"), Text( - "Average Price: ${originStat.average_price.toStringAsFixed(2)} €"), + "Average Price: ${originStat.averagePrice.toStringAsFixed(2)} €"), ]); }).toList() ], diff --git a/lib/pages/transaction.dart b/lib/pages/transaction.dart index acad5dc..79e674d 100644 --- a/lib/pages/transaction.dart +++ b/lib/pages/transaction.dart @@ -132,12 +132,15 @@ class _TransactionPageState extends State { children: [ // todo : human names Text( - "${API().getItem(transaction.item).name}", + API().getItem(transaction.item).name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 28), ), Text( - "${API().getItem(transaction.item).variants[transaction.variant]!.name}", + API() + .getItem(transaction.item) + .variants[transaction.variant]! + .name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, @@ -355,7 +358,7 @@ class TransactionSelectPage extends StatelessWidget { body: ListView( children: selectionList.isEmpty ? [ - ListTile( + const ListTile( title: Center(child: Text("No Transactions available")), ) ] From 61134f7b63d13a9238a1575c43e1175a22edbfd8 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Tue, 8 Oct 2024 13:47:17 +0200 Subject: [PATCH 08/10] fix --- lib/pages/stats.dart | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/pages/stats.dart b/lib/pages/stats.dart index cb99e2a..80ecceb 100644 --- a/lib/pages/stats.dart +++ b/lib/pages/stats.dart @@ -59,16 +59,26 @@ class StatsPage extends StatelessWidget { )), if (min.isNotEmpty) - const ListTile(title: Text("Items under Minimum")), + const ListTile( + title: Text( + "Items under Minimum", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + )), ...min.map((item) { + var (itemID, variant) = itemVariant(item.itemVariant); + var name = API().getItem(itemID).variants[variant]!.name; + return ListTile( - title: Text( - "Item ${item.itemVariant} under minimum. Needs ${item.need} more."), + title: Text("$name under minimum. Needs ${item.need} more."), ); }).toList(), if (expired.isNotEmpty) - const ListTile(title: Text("Expired Items")), + const ListTile( + title: Text( + "Expired Items", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + )), // Mapping expired list to widgets ...expired.map((item) { From 7ee1cda550c70b6f541fb62c67433d8c83765344 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Fri, 18 Oct 2024 11:02:28 +0200 Subject: [PATCH 09/10] image --- lib/api.dart | 2 ++ lib/pages/itemview.dart | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index 1ae5dc6..86c9678 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -361,12 +361,14 @@ class FlowInfo { class Item { late String id; + late String? image; late String name; late String? category; late Map variants; Item(Map json) { id = json["uuid"]; + image = json["image"]; name = json["name"]; category = json["category"]; variants = {}; diff --git a/lib/pages/itemview.dart b/lib/pages/itemview.dart index a6e7363..8ea7415 100644 --- a/lib/pages/itemview.dart +++ b/lib/pages/itemview.dart @@ -43,12 +43,15 @@ class _ItemViewState extends State { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - const Align( + Align( alignment: Alignment.centerLeft, - child: Placeholder( - fallbackWidth: 100, - fallbackHeight: 100, - ), + child: widget.item.image != null + ? Image.network( + "${API().instance}/${widget.item.image}", + height: 100, + width: 100, + ) + : null, ), const SizedBox( width: 16.0, From e366960ce7d220399d28a4062b72b74163bb7d2c Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sat, 19 Oct 2024 21:15:02 +0200 Subject: [PATCH 10/10] fix --- lib/api.dart | 2 +- lib/pages/consume.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index 86c9678..477df71 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -201,7 +201,7 @@ class API { // /demand Future consumeItem( - String transaction, String destination, String price) async { + String transaction, String destination, double price) async { await postRequest("$instance/demand", {"uuid": transaction, "destination": destination, "price": price}); } diff --git a/lib/pages/consume.dart b/lib/pages/consume.dart index 9888126..d3f03e2 100644 --- a/lib/pages/consume.dart +++ b/lib/pages/consume.dart @@ -27,8 +27,8 @@ class _ConsumePageState extends State { _formKey.currentState!.save(); API() - .consumeItem( - widget.transaction.uuid, _selectedDestination, "$_price €") + .consumeItem(widget.transaction.uuid, _selectedDestination, + double.parse(_price)) .then((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item consumed successfully!')),