sync api + ui refactor

This commit is contained in:
JMARyA 2024-09-26 21:35:28 +02:00
parent a608c50447
commit 2f24e02696
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
12 changed files with 198 additions and 227 deletions

View file

@ -20,6 +20,25 @@ class API {
API._internal(); 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 { Future<void> init() async {
pref = await SharedPreferences.getInstance(); pref = await SharedPreferences.getInstance();
instance = pref.getString("instance") ?? ""; instance = pref.getString("instance") ?? "";
@ -60,14 +79,7 @@ class API {
} }
// /items // /items
Future<List<Item>> getItems() async { List<Item> getItems() {
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();
return items!; return items!;
} }
@ -90,23 +102,12 @@ class API {
return ret; return ret;
} }
Future<Map<String, Location>> getLocations() async { Map<String, Location> getLocations() {
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)));
return locations!; return locations!;
} }
Future<Item> getItem(String item) async { Item getItem(String item) {
if (items != null) { return items!.firstWhere((x) => x.id == item);
return items!.firstWhere((x) => x.id == item);
}
return Item(jsonDecode(await getRequest("$instance/item/$item")));
} }
Future<Transaction> getTransaction(String id) async { Future<Transaction> getTransaction(String id) async {
@ -236,25 +237,13 @@ class API {
// Flows // Flows
// /flows // /flows
Future<Map<String, FlowInfo>> getFlows() async { Map<String, FlowInfo> getFlows() {
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)));
return flowInfos!; return flowInfos!;
} }
// /flow/<id>/info // /flow/<id>/info
Future<FlowInfo> getFlowInfo(String id) async { FlowInfo getFlowInfo(String id) {
if (flowInfos != null) { return flowInfos![id]!;
return flowInfos![id]!;
}
return FlowInfo(jsonDecode(await getRequest("$instance/flow/$id/info")));
} }
Future<Flow> getFlow(String id) async { Future<Flow> getFlow(String id) async {
@ -277,12 +266,20 @@ class API {
} }
// /flow/<id>/end // /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", var resp = jsonDecode(await postRequest("$instance/flow/$id/end",
{"produced": produced?.map((x) => x.json()).toList()})); {"produced": produced?.map((x) => x.json()).toList()}));
print(resp);
if (produced != null) { 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; return null;
@ -314,13 +311,8 @@ class API {
"$instance/transaction/$id/move", {"to": newLocation})); "$instance/transaction/$id/move", {"to": newLocation}));
} }
Future<Location> getLocation(String id) async { Location getLocation(String id) {
if (locations != null) { return locations![id]!;
return locations![id]!;
}
var resp = jsonDecode(await getRequest("$instance/location/$id"));
return Location(resp);
} }
Future<String> addNoteToFlow(String flowID, String content) async { Future<String> addNoteToFlow(String flowID, String content) async {

View file

@ -8,6 +8,9 @@ import 'package:flutter/material.dart';
Future<void> main() async { Future<void> main() async {
await API().init(); await API().init();
if (API().isInit()) {
await API().prefetch();
}
runApp(const MyApp()); runApp(const MyApp());
} }

View file

@ -67,17 +67,16 @@ class _ActiveFlowPageState extends State<ActiveFlowPage> {
if (widget.info.next != null) if (widget.info.next != null)
IconButton( IconButton(
onPressed: () { onPressed: () {
API.API().getFlowInfo(widget.info.next!).then((newInfo) { var newInfo = API.API().getFlowInfo(widget.info.next!);
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(MaterialPageRoute(
builder: (context) { builder: (context) {
return CreateFlowPage( return CreateFlowPage(
newInfo, newInfo,
() {}, () {},
previousFlow: widget.flow, previousFlow: widget.flow,
); );
}, },
)); ));
});
}, },
icon: const Icon(Icons.arrow_forward)), icon: const Icon(Icons.arrow_forward)),
], ],

View file

@ -25,11 +25,10 @@ class _CreateFlowPageState extends State<CreateFlowPage> {
input: depends.map((x) => x.uuid).toList()) input: depends.map((x) => x.uuid).toList())
.then((x) { .then((x) {
API.API().getFlow(x).then((flow) { API.API().getFlow(x).then((flow) {
API.API().getFlowInfo(flow.kind).then((info) { var info = API.API().getFlowInfo(flow.kind);
Navigator.of(context).pushReplacement(MaterialPageRoute( Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => ActiveFlowPage(flow, info), builder: (context) => ActiveFlowPage(flow, info),
)); ));
});
}); });
}); });
return; return;
@ -73,11 +72,13 @@ class _CreateFlowPageState extends State<CreateFlowPage> {
return [ return [
Column( Column(
children: widget.info.depends.map((x) { children: widget.info.depends.map((x) {
var (item, variant) = API.itemVariant(x);
return ElevatedButton( return ElevatedButton(
onPressed: () { onPressed: () {
selectDependItems(context, x); selectDependItems(context, x);
}, },
child: Text("Add $x")); child:
Text("Add ${API.API().getItem(item).variants[variant]!.name}"));
}).toList()), }).toList()),
const Divider(), const Divider(),
]; ];
@ -90,7 +91,7 @@ class _CreateFlowPageState extends State<CreateFlowPage> {
Card( Card(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), 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)), Card(child: Icon(Icons.arrow_right)),

View file

@ -23,13 +23,7 @@ class _EndFlowWithProduceState extends State<EndFlowWithProduce> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
API.API().getLocations().then( locations = API.API().getLocations();
(value) {
setState(() {
locations = value;
});
},
);
} }
refresh() { refresh() {
@ -46,25 +40,24 @@ class _EndFlowWithProduceState extends State<EndFlowWithProduce> {
List<Widget> ret = []; List<Widget> ret = [];
for (var i in widget.info.produces!) { for (var i in widget.info.produces!) {
var (itemID, variant) = API.itemVariant(i);
var item = API.API().getItem(itemID);
ret.add(ElevatedButton( ret.add(ElevatedButton(
onPressed: () { onPressed: () {
var (itemID, variant) = API.itemVariant(i); Navigator.of(context).push(MaterialPageRoute(
API.API().getItem(itemID).then((item) { builder: (context) {
Navigator.of(context).push(MaterialPageRoute( return SupplyPage(
builder: (context) { item,
return SupplyPage( refresh,
item, onlyVariants: [variant],
refresh, forcePrice: "0.00",
onlyVariants: [variant], forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}",
forcePrice: "0.00", onCreate: addProduced,
forceOrigin: "flow::${widget.flow.kind}::${widget.flow.id}", );
onCreate: addProduced, },
); ));
},
));
});
}, },
child: Text("Produced $i"))); child: Text("Produced ${item.variants[variant]!.name}")));
} }
return ret; return ret;

View file

@ -24,13 +24,17 @@ class _FlowInfoPageState extends State<FlowInfoPage> {
body: Column( body: Column(
children: [ children: [
// todo : ui improve // 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) if (widget.info.depends.isNotEmpty)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
const Text("Flow can use: "), 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) if (widget.info.produces?.isNotEmpty ?? false)
@ -38,7 +42,10 @@ class _FlowInfoPageState extends State<FlowInfoPage> {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
const Text("Flow can produce: "), 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(), const Divider(),

View file

@ -20,13 +20,7 @@ class _FlowsPageState extends State<FlowsPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
API.API().getFlows().then( flowInfos = API.API().getFlows();
(value) {
setState(() {
flowInfos = value;
});
},
);
} }
Widget flowTile(BuildContext context, API.FlowInfo x) { Widget flowTile(BuildContext context, API.FlowInfo x) {
@ -94,7 +88,8 @@ class _FlowsPageState extends State<FlowsPage> {
children: producedMapping[key]!.map((x) { children: producedMapping[key]!.map((x) {
return flowTile(context, x); return flowTile(context, x);
}).toList()); }).toList());
items.add(ExpandableListItem(body: flows, header: Text(key))); items.add(ExpandableListItem(
body: flows, header: Text(API.API().getItem(key).name)));
} }
return ExpandableList(items); return ExpandableList(items);
@ -122,7 +117,8 @@ class _FlowsPageState extends State<FlowsPage> {
children: dependsMapping[key]!.map((x) { children: dependsMapping[key]!.map((x) {
return flowTile(context, x); return flowTile(context, x);
}).toList()); }).toList());
items.add(ExpandableListItem(body: flows, header: Text(key))); items.add(ExpandableListItem(
body: flows, header: Text(API.API().getItem(key).name)));
} }
return ExpandableList(items); return ExpandableList(items);
@ -169,13 +165,12 @@ class _FlowsPageState extends State<FlowsPage> {
code = code!.replaceFirst("Code scanned = ", ""); code = code!.replaceFirst("Code scanned = ", "");
API.API().getFlow(code).then((flow) { API.API().getFlow(code).then((flow) {
API.API().getFlowInfo(flow.kind).then((info) { var info = API.API().getFlowInfo(flow.kind);
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(MaterialPageRoute(
builder: (context) { builder: (context) {
return ActiveFlowPage(flow, info); return ActiveFlowPage(flow, info);
}, },
)); ));
});
}); });
}, },
); );

View file

@ -13,20 +13,10 @@ class ItemsPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: const Text("Items"), title: const Text("Items"),
), ),
body: FutureBuilder( body: ListView(
future: API().getItems(), children: API().getItems().map((x) {
builder: (context, snapshot) { return ItemTile(x);
if (!snapshot.hasData) { }).toList()),
return const CircularProgressIndicator();
}
var items = snapshot.data!;
return ListView(
children: items.map((x) {
return ItemTile(x);
}).toList());
}),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () {
// scan transaction code // scan transaction code

View file

@ -17,13 +17,7 @@ class _LocationsPageState extends State<LocationsPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
API().getLocations().then( locations = API().getLocations();
(value) {
setState(() {
locations = value;
});
},
);
} }
TreeNode buildTree(BuildContext context, String locID) { TreeNode buildTree(BuildContext context, String locID) {

View file

@ -31,6 +31,8 @@ class _SetupPageState extends State<SetupPage> {
const SnackBar(content: Text('Setup Complete!')), const SnackBar(content: Text('Setup Complete!')),
); );
await API().prefetch();
// Navigate or close the setup screen // Navigate or close the setup screen
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,

View file

@ -126,7 +126,7 @@ class _SupplyPageState extends State<SupplyPage> {
const SizedBox(height: 16), const SizedBox(height: 16),
// Origin Field with Dropdown and Text Input // Origin Field with Dropdown and Text Input
if (widget.forceOrigin == null) if (widget.forceOrigin == null) ...[
AutocompletedTextField( AutocompletedTextField(
options: origins, options: origins,
getValue: () => _selectedOrigin, getValue: () => _selectedOrigin,
@ -146,11 +146,11 @@ class _SupplyPageState extends State<SupplyPage> {
}); });
}, },
label: "Origin"), label: "Origin"),
const SizedBox(height: 16),
const SizedBox(height: 16), ],
// Price Field // Price Field
if (widget.forcePrice == null) if (widget.forcePrice == null) ...[
TextFormField( TextFormField(
decoration: const InputDecoration(labelText: 'Price'), decoration: const InputDecoration(labelText: 'Price'),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
@ -165,8 +165,8 @@ class _SupplyPageState extends State<SupplyPage> {
return null; return null;
}, },
), ),
const SizedBox(height: 16),
const SizedBox(height: 16), ],
// Location Dropdown // Location Dropdown
Row( Row(

View file

@ -40,84 +40,80 @@ class _TransactionPageState extends State<TransactionPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(widget.transaction.item), title: Text(API().getItem(transaction.item).name),
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
API().getLocations().then((locations) { var locations = API().getLocations();
List<String> locationList = locations.keys.toList(); List<String> locationList = locations.keys.toList();
String? selectedLocationID; String? selectedLocationID;
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: const Text('Move Transaction'), title: const Text('Move Transaction'),
content: Row( content: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
DropdownButton<String>( DropdownButton<String>(
value: selectedLocationID, value: selectedLocationID,
onChanged: (value) { onChanged: (value) {
selectedLocationID = value!; selectedLocationID = value!;
API() API()
.moveTransaction(widget.transaction.uuid, .moveTransaction(widget.transaction.uuid,
selectedLocationID!) selectedLocationID!)
.then((x) { .then((x) {
Navigator.of(context).pop(); 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(() {}); setState(() {});
}, },
items: locationList icon: const Icon(Icons.qr_code))
.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.move_up)) icon: const Icon(Icons.move_up))
], ],
@ -143,12 +139,12 @@ class _TransactionPageState extends State<TransactionPage> {
children: [ children: [
// todo : human names // todo : human names
Text( Text(
"${transaction.item}", "${API().getItem(transaction.item).name}",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 28), fontWeight: FontWeight.bold, fontSize: 28),
), ),
Text( Text(
"${transaction.variant}", "${API().getItem(transaction.item).variants[transaction.variant]!.name}",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 20, fontSize: 20,
@ -262,14 +258,14 @@ class TransactionCard extends StatelessWidget {
Row( Row(
children: [ children: [
Text( Text(
t.item, API().getItem(t.item).name,
style: const TextStyle(fontSize: 16), style: const TextStyle(fontSize: 16),
), ),
const SizedBox( const SizedBox(
width: 4, width: 8,
), ),
Text( Text(
t.variant, API().getItem(t.item).variants[t.variant]!.name,
style: TextStyle(fontSize: 14, color: Colors.grey[400]), style: TextStyle(fontSize: 14, color: Colors.grey[400]),
), ),
], ],
@ -358,29 +354,28 @@ class TransactionSelectPage extends StatelessWidget {
selectionList.add(s); selectionList.add(s);
} }
if (selectionList.isEmpty) {
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No Transactions to select')),
);
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Select a Transaction"), title: const Text("Select a Transaction"),
), ),
body: ListView( body: ListView(
children: selectionList children: selectionList.isEmpty
.map((x) => TransactionCard( ? [
x, ListTile(
() {}, title: Center(child: Text("No Transactions available")),
onLongPress: (x) {}, )
onTap: (t) { ]
onSelect(t); : selectionList
Navigator.of(context).pop(); .map((x) => TransactionCard(
}, x,
)) () {},
.toList()), onLongPress: (x) {},
onTap: (t) {
onSelect(t);
Navigator.of(context).pop();
},
))
.toList()),
); );
} }
} }