use std::collections::HashMap; use dioxus::signals::{Readable, Writable}; use serde_json::json; use crate::setup::Credentials; use crate::store::{load_from_local_storage, save_to_local_storage}; use crate::try_recover_api; use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::Client; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; pub fn get_item(item: &str) -> Option { if let Some(api) = crate::API.read().as_ref() { api.get_item(item.to_string()) } else { try_recover_api().get_item(item.to_string()) } } pub async fn api_get_auth(path: String) -> Result where T: DeserializeOwned, { let creds = load_from_local_storage::("creds").unwrap_or_default(); let token = creds.token.as_str(); let instance = creds.instance_url.as_str(); let url = format!("{instance}{path}"); let mut headers = HeaderMap::new(); headers.insert("Token", HeaderValue::from_str(token).unwrap()); let client = Client::new(); let res = client .get(&url) .headers(headers) .send() .await? .error_for_status()? .json::() .await?; Ok(res) } pub async fn api_post_auth(path: String, body: X) -> Result where T: DeserializeOwned, X: Serialize, { let creds = load_from_local_storage::("creds").unwrap_or_default(); let token = creds.token.as_str(); let instance = creds.instance_url.as_str(); let url = format!("{instance}{path}"); let mut headers = HeaderMap::new(); headers.insert("Token", HeaderValue::from_str(token).unwrap()); let client = Client::new(); let res = client .post(&url) .headers(headers) .json(&body) .send() .await? .error_for_status()? .json::() .await?; Ok(res) } #[derive(Debug)] pub struct API { pub instance: String, pub items: Vec, pub locations: HashMap, pub flow_info: HashMap, } impl API { pub async fn new(instance: String) -> Self { let mut items: HashMap> = api_get_auth("/items".to_string()).await.unwrap(); let items = items.remove("items").unwrap(); let locations: HashMap = api_get_auth("/locations".to_string()).await.unwrap(); let flow_info: HashMap = api_get_auth("/flows".to_string()).await.unwrap(); save_to_local_storage("api_items", items.clone()); save_to_local_storage("api_locations", locations.clone()); save_to_local_storage("api_flow_info", flow_info.clone()); Self { instance, items, locations, flow_info, } } pub fn try_recover() -> Self { Self { instance: load_from_local_storage::("creds") .unwrap_or_default() .instance_url, items: load_from_local_storage("api_items").unwrap_or_default(), locations: load_from_local_storage("api_locations").unwrap_or_default(), flow_info: load_from_local_storage("api_flow_info").unwrap_or_default(), } } pub async fn get_items(&self) -> &[Item] { &self.items } pub async fn get_global_item_stat() -> GlobalItemStat { api_get_auth("/items/stat".to_string()).await.unwrap() } pub async fn get_unique_field(item: String, variant: String, field: String) -> Vec { api_get_auth(format!("/item/{item}/{variant}/unique?field={field}")) .await .unwrap() } pub fn get_locations(&self) -> &HashMap { &self.locations } pub fn get_item(&self, item: String) -> Option { self.items.iter().find(|x| x.uuid == item).cloned() } pub async fn get_transaction(id: String) -> Transaction { api_get_auth(format!("/transaction/{id}")).await.unwrap() } pub async fn get_transactions_of_location( location: String, recursive: bool, ) -> Vec { let mut url = format!("/location/{location}/inventory"); if recursive { url.push_str("?recursive=true"); } api_get_auth(url).await.unwrap() } pub async fn get_consumed_items( item: String, variant: String, destination: Option, ) -> Vec { let mut url = format!("/item/{item}/{variant}/demand"); if let Some(dest) = destination { url.push_str(&format!("?destination={dest}")); } api_get_auth(url).await.unwrap() } pub async fn get_inventory(item: String, origin: Option) -> Vec { let mut url = format!("/item/{item}/inventory"); if let Some(origin) = origin { url.push_str(&format!("?origin={origin}")); } api_get_auth(url).await.unwrap() } pub async fn get_inventory_of_variant(item: String, variant: String) -> Vec { api_get_auth(format!("/item/{item}/{variant}/inventory")) .await .unwrap() } pub async fn supply_item( item: String, variant: String, price: f64, origin: Option, location: Option, note: Option, ) -> String { let res: serde_json::Value = api_post_auth( "/supply".to_string(), json!({ "item": item, "variant": variant, "price": price, "origin": origin, "location": location, "note": note, "properties": serde_json::Value::Null, "quanta": serde_json::Value::Null }), ) .await .unwrap(); res.as_object() .unwrap() .get("uuid") .unwrap() .as_str() .unwrap() .to_string() } pub async fn consume_item(transaction: String, destination: String, price: f64) { api_post_auth::, _>( "/demand".to_string(), json!({ "uuid": transaction, "destination": destination, "price": price }), ) .await .unwrap(); } pub async fn get_stat(item: String, variant: String, full: bool) -> ItemVariantStat { api_get_auth(format!( "/item/{item}/{variant}/stat{}", if full { "?full=true" } else { "" } )) .await .unwrap() } pub fn get_url_instance(&self, item: String) -> String { format!("{}/{item}", self.instance) } pub async fn get_price_history( item: String, variant: String, origin: Option, ) -> Vec { let mut url = format!("/item/{item}/{variant}/price_history"); if let Some(origin) = origin { url.push_str(&format!("?origin={origin}")); } api_get_auth(url).await.unwrap() } pub async fn get_latest_price(item: String, variant: String, origin: Option) -> f64 { let mut url = format!("/item/{item}/{variant}/price_latest"); if let Some(origin) = origin { url.push_str(&format!("?origin={origin}")); } api_get_auth(url).await.unwrap() } pub async fn get_flows(&self) -> &HashMap { &self.flow_info } pub async fn get_flow_info(&self, id: String) -> Option<&FlowInfo> { self.flow_info.iter().find(|x| *x.0 == id).map(|x| x.1) } pub async fn get_flow(id: String) -> Flow { api_get_auth(format!("/flow/{id}")).await.unwrap() } pub async fn get_active_flows_of(id: String) -> Vec { api_get_auth(format!("/flow/{id}/active")).await.unwrap() } pub async fn start_flow(id: String, input: Option>) -> String { let res: serde_json::Value = api_post_auth(format!("/flow/{id}"), json!({"input": input})) .await .unwrap(); res.as_object() .unwrap() .get("uuid") .unwrap() .as_str() .unwrap() .to_string() } /* pub async fn end_flow(id: String, produced: Option>) -> HashMap> { todo!() } // /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; } */ pub async fn continue_flow(id: String, input: Option>) -> String { let res: serde_json::Value = api_post_auth(format!("/flow/{id}/continue"), json!({"input": input})) .await .unwrap(); res.as_object() .unwrap() .get("uuid") .unwrap() .as_str() .unwrap() .to_string() } pub async fn get_expired_items() -> Vec { api_get_auth("/items/expired".to_string()).await.unwrap() } pub async fn get_items_under_min() -> Vec { api_get_auth("/items/min".to_string()).await.unwrap() } pub async fn move_transaction(id: String, new_location: String) { api_post_auth( format!("/transaction/{id}/move"), json!({"to": new_location}), ) .await .unwrap() } pub fn get_location(&self, id: String) -> Option<&Location> { self.locations.get(&id) } pub async fn add_note_to_flow(flow_id: String, content: String) -> String { let res: serde_json::Value = api_post_auth(format!("/flow/{flow_id}/note"), json!({"content": content})) .await .unwrap(); res.as_object() .unwrap() .get("uuid") .unwrap() .as_str() .unwrap() .to_string() } pub async fn get_notes_of_flow(flow_id: String) -> Vec { api_get_auth(format!("/flow/{flow_id}/notes")) .await .unwrap() } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct FlowInfo { pub id: String, pub name: String, pub depends: Vec, pub next: Option, pub produces: Option>, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Item { pub uuid: String, pub image: Option, pub name: String, pub category: Option, pub variants: HashMap, } impl Item { pub fn get_variant(&self, variant: &str) -> Option { let var = self.variants.iter().find(|x| *x.0 == variant)?; Some(var.1.clone()) } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct ItemVariant { pub item: String, pub variant: String, pub name: String, pub min: Option, pub expiry: Option, pub barcodes: Option>, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Transaction { pub uuid: String, pub item: String, pub variant: String, pub price: f64, pub origin: Option, pub timestamp: i64, pub consumed: Option, pub expired: bool, pub note: Option, pub quanta: Option, pub properties: Option, pub location: Option, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct ConsumeInfo { pub destination: String, pub price: f64, pub timestamp: i64, } #[derive(Debug, Serialize, Deserialize)] pub struct ItemVariantStat { pub amount: i64, pub total_price: f64, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Location { pub id: String, pub name: String, pub parent: Option, pub conditions: Option, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct LocationCondition { pub temperature: String, } #[derive(Debug, Serialize, Deserialize)] pub struct MinItem { pub item_variant: String, pub need: i64, } #[derive(Debug, Serialize, Deserialize)] pub struct Flow { pub id: String, pub started: i64, pub kind: String, pub input: Option>, pub done: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct FlowDone { pub ended: i64, pub next: Option, pub produced: Vec, } #[derive(Debug, Serialize, Deserialize)] pub struct FlowNote { pub uuid: String, pub timestamp: i64, pub content: String, pub on_flow: String, } #[derive(Debug, Serialize, Deserialize)] pub struct GlobalItemStat { pub item_count: i64, pub total_transactions: i64, pub total_price: f64, } #[derive(Debug, Serialize, Deserialize)] pub struct FullItemVariantStat { pub amount: i64, pub total_price: f64, pub expiry_rate: f64, pub origins: HashMap, } #[derive(Debug, Serialize, Deserialize)] pub struct OriginStat { pub average_price: f64, pub inventory: i64, }