From a608c504475e290a4dcf77b0ee18be5dc12d6ad6 Mon Sep 17 00:00:00 2001
From: JMARyA <jmarya@hydrar.de>
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<ActiveFlowPage> {
                   ),
                 );
 
-                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<CreateFlowPage> {
     });
   }
 
-  Widget buildInputSelection(BuildContext context) {
-    return Column(
-        children: widget.info.depends.map((x) {
-      return ElevatedButton(
-          onPressed: () {
-            selectDependItems(context, x);
-          },
-          child: Text("Add $x"));
-    }).toList());
+  List<Widget> 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<CreateFlowPage> {
   @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 <jmarya@hydrar.de>
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<void> prefetch() async {
+    // todo : prefetch
+    // fetch items
+    var resp = jsonDecode(await getRequest("$instance/items"));
+    var lst = resp["items"] as List<dynamic>;
+    items = lst.map((x) => Item(x)).toList();
+
+    // fetch locations
+    var locResp = jsonDecode(await getRequest("$instance/locations"))
+        as Map<String, dynamic>;
+    locations = locResp.map((key, value) => MapEntry(key, Location(value)));
+
+    // fetch flowInfos
+    var flowResp =
+        jsonDecode(await getRequest("$instance/flows")) as Map<String, dynamic>;
+
+    flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value)));
+  }
+
   Future<void> init() async {
     pref = await SharedPreferences.getInstance();
     instance = pref.getString("instance") ?? "";
@@ -60,14 +79,7 @@ class API {
   }
 
   // /items
-  Future<List<Item>> getItems() async {
-    if (items != null) {
-      return items!;
-    }
-
-    var resp = jsonDecode(await getRequest("$instance/items"));
-    var lst = resp["items"] as List<dynamic>;
-    items = lst.map((x) => Item(x)).toList();
+  List<Item> getItems() {
     return items!;
   }
 
@@ -90,23 +102,12 @@ class API {
     return ret;
   }
 
-  Future<Map<String, Location>> getLocations() async {
-    if (locations != null) {
-      return locations!;
-    }
-
-    var resp = jsonDecode(await getRequest("$instance/locations"))
-        as Map<String, dynamic>;
-    locations = resp.map((key, value) => MapEntry(key, Location(value)));
+  Map<String, Location> getLocations() {
     return locations!;
   }
 
-  Future<Item> 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<Transaction> getTransaction(String id) async {
@@ -236,25 +237,13 @@ class API {
   // Flows
 
   // /flows
-  Future<Map<String, FlowInfo>> getFlows() async {
-    if (flowInfos != null) {
-      return flowInfos!;
-    }
-
-    var resp =
-        jsonDecode(await getRequest("$instance/flows")) as Map<String, dynamic>;
-
-    flowInfos = resp.map((key, value) => MapEntry(key, FlowInfo(value)));
+  Map<String, FlowInfo> getFlows() {
     return flowInfos!;
   }
 
   // /flow/<id>/info
-  Future<FlowInfo> 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<Flow> getFlow(String id) async {
@@ -277,12 +266,20 @@ class API {
   }
 
   // /flow/<id>/end
-  Future<List<String>?> endFlow(String id, {List<SupplyForm>? produced}) async {
+  Future<Map<String, List<String>>?> endFlow(String id,
+      {List<SupplyForm>? 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<dynamic>).cast<String>();
+      var produced = resp["produced"] as Map<String, dynamic>;
+      return produced.map(
+        (key, value) {
+          return MapEntry(key, (value as List<dynamic>).cast<String>());
+        },
+      );
     }
 
     return null;
@@ -314,13 +311,8 @@ class API {
         "$instance/transaction/$id/move", {"to": newLocation}));
   }
 
-  Future<Location> 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<String> 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<void> 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<ActiveFlowPage> {
           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<CreateFlowPage> {
               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<CreateFlowPage> {
     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<CreateFlowPage> {
         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<EndFlowWithProduce> {
   @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<EndFlowWithProduce> {
     List<Widget> 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<FlowInfoPage> {
       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<FlowInfoPage> {
               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<FlowsPage> {
   @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<FlowsPage> {
           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<FlowsPage> {
           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<FlowsPage> {
                   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<LocationsPage> {
   @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<SetupPage> {
       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<SupplyPage> {
                   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<SupplyPage> {
                           });
                         },
                         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<SupplyPage> {
                         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<TransactionPage> {
   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<String> locationList = locations.keys.toList();
-                  String? selectedLocationID;
+                var locations = API().getLocations();
+                List<String> 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<String>(
-                              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<String>(
+                            value: selectedLocationID,
+                            onChanged: (value) {
+                              selectedLocationID = value!;
+                              API()
+                                  .moveTransaction(widget.transaction.uuid,
+                                      selectedLocationID!)
+                                  .then((x) {
+                                Navigator.of(context).pop();
+                              });
 
+                              setState(() {});
+                            },
+                            items: locationList
+                                .map<DropdownMenuItem<String>>((locationID) {
+                              return DropdownMenuItem<String>(
+                                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<DropdownMenuItem<String>>((locationID) {
-                                return DropdownMenuItem<String>(
-                                  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<TransactionPage> {
                   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 <jmarya@hydrar.de>
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/<item_id>/<variant_id>/stat
-  Future<ItemVariantStat> getStat(String item, String variant) async {
+  Future<dynamic> 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<String, OriginStat> origins;
+
+  FullItemVariantStat(Map<String, dynamic> json) {
+    amount = json["amount"];
+    totalPrice = json["total_price"];
+    expiryRate = json["expiry_rate"];
+    origins = (json["origins"] as Map<String, dynamic>)
+        .map((key, value) => MapEntry(key, OriginStat(value)));
+  }
+}
+
+class OriginStat {
+  late double average_price;
+  late int inventory;
+
+  OriginStat(Map<String, dynamic> json) {
+    average_price = json["average_price"];
+    inventory = json["inventory"];
+  }
+}

From d21554cddcf9e6c0c0f81ed9d463bf298edd6c46 Mon Sep 17 00:00:00 2001
From: JMARyA <jmarya@hydrar.de>
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 <jmarya@hydrar.de>
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 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+	<uses-permission android:name="android.permission.INTERNET"/>
     <application
-        android:label="cdb_ui"
+        android:label="CDB"
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
         <activity
diff --git a/lib/api.dart b/lib/api.dart
index e42b99d..ee016ee 100644
--- a/lib/api.dart
+++ b/lib/api.dart
@@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
 // todo : api errors
 
 class API {
-  late SharedPreferences pref;
+  SharedPreferences? pref;
   static final API _instance = API._internal();
 
   // cache
@@ -41,16 +41,24 @@ class API {
 
   Future<void> 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: <String, String>{
       '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: <String, String>{
           '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<String> supplyItem(String item, String variant, String price,
+  Future<String> 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/<item_id>/<variant_id>/price_history?<origin>
-  Future<List<Price>> getPriceHistory(String item, String variant,
+  Future<List<double>> 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<dynamic>;
 
-    return resp.map((x) => Price(x)).toList();
+    return resp.map((x) => x as double).toList();
   }
 
-  Future<Price> getLatestPrice(String item, String variant,
+  Future<double> 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<String, dynamic>;
 
-    return Price(resp);
+    return resp as double;
   }
 
   // Flows
@@ -386,31 +394,11 @@ class ItemVariant {
   }
 }
 
-class Price {
-  late double value;
-  late String currency;
-
-  Price(Map<String, dynamic> 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<String, dynamic> 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<void> 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<MyApp> createState() => _MyAppState();
+}
+
+class _MyAppState extends State<MyApp> {
+  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<SupplyPage> {
         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<SupplyPage> {
       }
 
       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<SupplyPage> {
                               ? 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<TransactionPage> {
 
             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<TransactionPage> {
               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 <jmarya@hydrar.de>
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<void> init() async {
+  Future<void> 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<MyApp> {
   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<MyApp> {
             ? (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<FlowsPage> {
             _ => 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<LocationsPage> {
             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<String?> 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<SupplyPage> {
                         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<SupplyPage> {
   }
 
   Future<Map<String, dynamic>> _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<TransactionPage> {
                             }).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 <permission_handler_windows/permission_handler_windows_plugin.h>
 #include <qr_bar_code/qr_bar_code_plugin_c_api.h>
+#include <webview_windows/webview_windows_plugin.h>
 
 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 <jmarya@hydrar.de>
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<String, dynamic>;
       return produced.map(
@@ -385,6 +383,7 @@ class ItemVariant {
   late String name;
   int? min;
   int? expiry;
+  List<int>? barcodes;
 
   ItemVariant(Map<String, dynamic> 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<dynamic>).cast<int>()
+        : 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<String, dynamic> 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<String, dynamic> 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<ActiveFlowPage> {
         ],
       ),
       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<CreateFlowPage> {
             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<ItemView> {
                   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<LocationView> {
         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<TransactionPage> {
                   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 <jmarya@hydrar.de>
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 <jmarya@hydrar.de>
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<String, ItemVariant> variants;
 
   Item(Map<String, dynamic> json) {
     id = json["uuid"];
+    image = json["image"];
     name = json["name"];
     category = json["category"];
     variants = <String, ItemVariant>{};
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<ItemView> {
               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 <jmarya@hydrar.de>
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<void> 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<ConsumePage> {
       _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!')),