import 'dart:convert'; import 'package:cdb_ui/pages/supply.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; // todo : api errors class API { SharedPreferences? pref; static final API _instance = API._internal(); // cache List? items; Map? locations; Map? flowInfos; factory API() { return _instance; } API._internal(); Future prefetch() async { // todo : prefetch // fetch items var resp = jsonDecode(await getRequest("$instance/items")); var lst = resp["items"] as List; items = lst.map((x) => Item(x)).toList(); // fetch locations var locResp = jsonDecode(await getRequest("$instance/locations")) as Map; locations = locResp.map((key, value) => MapEntry(key, Location(value))); // fetch flowInfos var flowResp = jsonDecode(await getRequest("$instance/flows")) as Map; flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value))); } Future init(Function refresh) async { pref = await SharedPreferences.getInstance(); instance = pref!.getString("instance") ?? ""; refresh(); } bool isInit() { 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); this.instance = instance; } String instance = ""; Future getRequest(String url) async { var resp = await http.get(Uri.parse(url), headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json; charset=UTF-8', 'Token': pref!.getString("token")! }); return utf8.decode(resp.bodyBytes); } Future postRequest(String url, Map data) async { var resp = await http.post(Uri.parse(url), headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json; charset=UTF-8', 'Token': pref!.getString("token")! }, body: jsonEncode(data)); return utf8.decode(resp.bodyBytes); } // /items List getItems() { return items!; } Future getGlobalItemStat() async { var resp = jsonDecode(await getRequest("$instance/items/stat")); return GlobalItemStat(resp); } // /item///unique? Future> getUniqueField( String item, String variant, String field) async { var resp = jsonDecode( await getRequest("$instance/item/$item/$variant/unique?field=$field")); List ret = []; for (var e in resp as List) { ret.add(e); } return ret; } Map getLocations() { return locations!; } Item getItem(String item) { return items!.firstWhere((x) => x.id == item); } Future getTransaction(String id) async { return Transaction( jsonDecode(await getRequest("$instance/transaction/$id"))); } Future> getTransactionsOfLocation(String location, {bool recursive = true}) async { var url = "$instance/location/$location/inventory"; if (recursive) { url += "?recursive=true"; } var resp = jsonDecode(await getRequest(url)) as List; return resp.map((x) => Transaction(x)).toList(); } Future> getConsumedItems(String item, String variant, {String? destination}) async { var url = "$instance/item/$item/$variant/demand"; if (destination != null) { url += "?destination=$destination"; } var resp = jsonDecode(await getRequest(url)) as List; return resp.map((x) => Transaction(x)).toList(); } // /item//inventory? Future> getInventory(String item, {String? origin}) async { var url = "$instance/item/$item/inventory"; if (origin != null) { url += "?origin=$origin"; } var resp = jsonDecode(await getRequest(url)) as List; return resp.map((x) => Transaction(x)).toList(); } // /item///inventory Future> getInventoryOfVariant( String item, String variant) async { var resp = jsonDecode(await getRequest("$instance/item/$item/$variant/inventory")) as List; return resp.map((x) => Transaction(x)).toList(); } // /supply Future supplyItem(String item, String variant, double price, String? origin, String? location, String? note) async { if (origin!.isEmpty) { origin = null; } if (location!.isEmpty) { location = null; } if (note!.isEmpty) { note = null; } var req = await postRequest("$instance/supply", { "item": item, "variant": variant, "price": price, "origin": origin, "location": location, "note": note }); var resp = jsonDecode(req); return resp["uuid"]; } // /demand Future consumeItem( String transaction, String destination, double price) async { await postRequest("$instance/demand", {"uuid": transaction, "destination": destination, "price": price}); } // /item///stat Future getStat(String item, String variant, {bool full = false}) async { if (full) { return FullItemVariantStat(jsonDecode( await getRequest("$instance/item/$item/$variant/stat?full=true"))); } return ItemVariantStat( jsonDecode(await getRequest("$instance/item/$item/$variant/stat"))); } String getImageURL(String item) { return "$instance/$item/image"; } // /item///price_history? Future> getPriceHistory(String item, String variant, {String? origin}) async { var url = "$instance/item/$item/$variant/price_history"; if (origin != null) { url += "?origin=$origin"; } var resp = jsonDecode(await getRequest(url)) as List; return resp.map((x) => x as double).toList(); } Future getLatestPrice(String item, String variant, {String? origin}) async { var url = "$instance/item/$item/$variant/price_latest"; if (origin != null) { url += "?origin=$origin"; } var resp = jsonDecode(await getRequest(url)) as Map; return resp as double; } // Flows // /flows Map getFlows() { return flowInfos!; } // /flow//info FlowInfo getFlowInfo(String id) { return flowInfos![id]!; } Future getFlow(String id) async { return Flow(jsonDecode(await getRequest("$instance/flow/$id"))); } Future> getActiveFlowsOf(String id) async { var res = jsonDecode(await getRequest("$instance/flow/$id/active")) as List; return res.map((x) => Flow(x)).toList(); } // /flow/ Future startFlow(String id, {List? input}) async { var resp = jsonDecode(await postRequest("$instance/flow/$id", {"input": input})); return resp["uuid"]; } // /flow//end Future>?> endFlow(String id, {List? produced}) async { var resp = jsonDecode(await postRequest("$instance/flow/$id/end", {"produced": produced?.map((x) => x.json()).toList()})); if (produced != null) { var produced = resp["produced"] as Map; return produced.map( (key, value) { return MapEntry(key, (value as List).cast()); }, ); } return null; } // /flow//continue Future continueFlow(String id, {List? input}) async { var resp = jsonDecode( await postRequest("$instance/flow/$id/continue", {"input": input})); return resp["uuid"]; } Future> getExpiredItems() async { var resp = jsonDecode(await getRequest("$instance/items/expired")) as List; return resp.map((x) => Transaction(x)).toList(); } Future> getItemsUnderMin() async { var resp = jsonDecode(await getRequest("$instance/items/min")) as List; return resp.map((x) => MinItem(x)).toList(); } Future moveTransaction(String id, String newLocation) async { jsonDecode(await postRequest( "$instance/transaction/$id/move", {"to": newLocation})); } Location getLocation(String id) { return locations![id]!; } Future addNoteToFlow(String flowID, String content) async { var res = jsonDecode( await postRequest("$instance/flow/$flowID/note", {"content": content})); return res["uuid"]; } Future> getNotesOfFlow(String flowID) async { var resp = jsonDecode(await getRequest("$instance/flow/$flowID/notes")) as List; return resp.map((x) => FlowNote(x)).toList(); } } class FlowInfo { late String id; late String name; late List depends; late String? next; late List? produces; FlowInfo(Map json) { id = json["id"]; name = json["name"]; depends = (json["depends"] as List).cast(); next = json["next"]; produces = json["produces"] != null ? (json["produces"] as List).cast() : null; } } class Item { late String id; late String? image; late String name; late String? category; late Map variants; Item(Map json) { id = json["uuid"]; image = json["image"]; name = json["name"]; category = json["category"]; variants = {}; json["variants"].forEach((key, value) { variants[key] = ItemVariant(value); }); } } class ItemVariant { late String item; late String variant; late String name; int? min; int? expiry; List? barcodes; ItemVariant(Map json) { item = json["item"]; variant = json["variant"]; name = json["name"]; min = json["min"]; expiry = json["expiry"]; barcodes = json["barcodes"] != null ? (json["barcodes"] as List).cast() : null; } } class Transaction { late String uuid; late String item; late String variant; late double price; String? origin; late int timestamp; ConsumeInfo? consumed; late bool expired; String? note; Location? location; Transaction(Map json) { uuid = json["uuid"]; item = json["item"]; variant = json["variant"]; price = json["price"]; origin = json["origin"]; timestamp = json["timestamp"]; expired = json["expired"]; note = json["note"]; consumed = json["consumed"] != null ? ConsumeInfo(json["consumed"]) : null; location = json["location"] != null ? Location(json["location"]) : null; } Transaction.inMemory(String itemID, String variantID, this.price, String? origin, Location? location, String? note) { uuid = ""; item = itemID; variant = variantID; origin = origin; timestamp = 0; consumed = null; expired = false; note = note; location = location; } } class ConsumeInfo { late String destination; late double price; late int timestamp; ConsumeInfo(Map json) { destination = json["destination"]; price = json["price"]; timestamp = json["timestamp"]; } } class ItemVariantStat { late int amount; late double totalPrice; ItemVariantStat(Map json) { amount = json["amount"]; totalPrice = json["total_price"]; } } class Location { late String id; late String name; late String? parent; late LocationCondition? conditions; Location.zero() { id = ""; name = ""; parent = null; conditions = null; } Location(Map json) { id = json["id"]; name = json["name"]; parent = json["parent"]; conditions = json["conditions"] != null ? (json["conditions"]) : null; } String fullNamePath(Map locations) { var name = this.name; if (parent != null) { name = "${locations[parent!]!.fullNamePath(locations)} / $name"; } return name; } } class LocationCondition { late String temperature; LocationCondition(Map json) { temperature = json["temperature"]; } } class MinItem { late String itemVariant; late int need; MinItem(Map json) { itemVariant = json["item_variant"]; need = json["need"]; } } class Flow { late String id; late int started; late String kind; late List? input; late FlowDone? done; Flow(Map json) { id = json["id"]; started = json["started"]; kind = json["kind"]; input = json["input"] != null ? (json["input"] as List).cast() : null; done = (json["done"] != null) ? FlowDone(json["done"]) : null; } } class FlowDone { late int ended; late String? next; late List? produced; FlowDone(Map json) { ended = json["ended"]; next = json["next"]; produced = json["produced"]; } } class FlowNote { late String uuid; late int timestamp; late String content; late String onFlow; FlowNote(Map json) { uuid = json["uuid"]; timestamp = json["timestamp"]; content = json["content"]; onFlow = json["on_flow"]; } } class GlobalItemStat { late int itemCount; late int totalTransactions; late double totalPrice; GlobalItemStat(Map json) { itemCount = json["item_count"]; totalTransactions = json["total_transactions"]; totalPrice = json["total_price"]; } } (String, String) itemVariant(String iv) { var split = iv.split("::"); return (split[0], split[1]); } class FullItemVariantStat { late int amount; late double totalPrice; late double expiryRate; late Map origins; FullItemVariantStat(Map json) { amount = json["amount"]; totalPrice = json["total_price"]; expiryRate = json["expiry_rate"]; origins = (json["origins"] as Map) .map((key, value) => MapEntry(key, OriginStat(value))); } } class OriginStat { late double averagePrice; late int inventory; OriginStat(Map json) { averagePrice = json["average_price"]; inventory = json["inventory"]; } }