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();
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 {

View file

@ -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());
}

View file

@ -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)),
],

View file

@ -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)),

View file

@ -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;

View file

@ -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(),

View file

@ -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);
},
));
});
},
);

View file

@ -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

View file

@ -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) {

View file

@ -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,

View file

@ -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(

View file

@ -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()),
);
}
}