diff --git a/.idea/Compware.iml b/.idea/Compware.iml new file mode 100644 index 0000000..cf84ae4 --- /dev/null +++ b/.idea/Compware.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6b84ce6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index fc59556..0543bb0 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -7,7 +7,12 @@ - + + + + + + + - { - "keyToString": { - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.git.unshallow": "true", - "RunOnceActivity.rust.reset.selective.auto.import": "true", - "git-widget-placeholder": "main", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "org.rust.cargo.project.model.PROJECT_DISCOVERY": "true", - "org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "", - "org.rust.first.attach.projects": "true", - "vue.rearranger.settings.migration": "true" + +}]]> diff --git a/Cargo.lock b/Cargo.lock index 6b389be..9ef3255 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -593,7 +593,7 @@ dependencies = [ "hashbrown 0.13.2", "instant", "once_cell", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -731,11 +731,13 @@ dependencies = [ "nostr-sdk", "serde", "serde_json", + "thiserror 2.0.9", "tokio", "uuid", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "zerofrom", ] [[package]] @@ -1142,7 +1144,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1163,7 +1165,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1621,7 +1623,7 @@ dependencies = [ "config", "regex", "serde", - "thiserror", + "thiserror 1.0.69", "typed-builder", ] @@ -1744,7 +1746,7 @@ dependencies = [ "serde-wasm-bindgen", "serde_json", "slotmap", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "wasm-bindgen", @@ -1776,7 +1778,7 @@ dependencies = [ "serde", "serde_json", "serde_qs 0.13.0", - "thiserror", + "thiserror 1.0.69", "tracing", "url", "wasm-bindgen", @@ -1796,7 +1798,7 @@ dependencies = [ "leptos_reactive", "serde", "server_fn", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -2002,7 +2004,7 @@ dependencies = [ "async-trait", "lru 0.12.5", "nostr", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -2020,7 +2022,7 @@ dependencies = [ "negentropy 0.4.3", "nostr", "nostr-database", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -2037,7 +2039,7 @@ dependencies = [ "nostr", "nostr-database", "nostr-relay-pool", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -2064,7 +2066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51ebcefb2f0b9a5e0bea115532c8ae4215d1b01eff176d0f4ba4192895c2708" dependencies = [ "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2440,7 +2442,7 @@ dependencies = [ "quote", "syn 2.0.90", "syn_derive", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2631,7 +2633,7 @@ checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2642,7 +2644,7 @@ checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2697,7 +2699,7 @@ dependencies = [ "serde_json", "serde_qs 0.12.0", "server_fn_macro_default", - "thiserror", + "thiserror 1.0.69", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -2877,7 +2879,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", ] [[package]] @@ -2891,6 +2902,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "time" version = "0.3.37" @@ -2994,7 +3016,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -3120,7 +3142,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] diff --git a/Cargo.toml b/Cargo.toml index b809cf5..f58b996 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,8 @@ gloo-net = "0.5" futures = "0.3" wasm-bindgen-futures = "0.4" serde_json="1.0.133" +thiserror = "2.0.9" +zerofrom = "0.1" [features] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] diff --git a/src/app.rs b/src/app.rs index 56c4c2a..57153ae 100644 --- a/src/app.rs +++ b/src/app.rs @@ -28,28 +28,25 @@ pub fn App() -> impl IntoView { } }); - // Function to add a new item from the grid - let add_item_from_grid = move || { - let new_id = Uuid::new_v4().to_string(); - - set_items.update(|items| { - let item = Item { - id: new_id.clone(), - name: String::new(), - description: String::new(), - tags: vec![], - reviews: vec![], - wikidata_id: None, - }; - items.push(item); - }); + // Handle adding a new item + let add_item = move || { + let new_item = Item { + id: Uuid::new_v4().to_string(), + name: "New Item".to_string(), + description: String::new(), + tags: vec![], + reviews: vec![], + wikidata_id: None, + }; + set_items.update(|items| items.push(new_item)); }; - + view! {

{ "CompareWare" }

- + +
} } diff --git a/src/components/editable_cell.rs b/src/components/editable_cell.rs new file mode 100644 index 0000000..72d6c1e --- /dev/null +++ b/src/components/editable_cell.rs @@ -0,0 +1,23 @@ +use leptos::*; + +#[component] +pub fn EditableCell( + value: String, + on_input: impl Fn(String) + 'static, +) -> impl IntoView { + let (input_value, set_input_value) = create_signal(value.clone()); + + let handle_input = move |e: web_sys::Event| { + let new_value = event_target_value(&e); + set_input_value.set(new_value.clone()); + on_input(new_value); + }; + + view! { + + } +} diff --git a/src/components/items_list.rs b/src/components/items_list.rs index 53940a6..ab09bc2 100644 --- a/src/components/items_list.rs +++ b/src/components/items_list.rs @@ -1,144 +1,186 @@ -use crate::models::item::Item; -use gloo_net::http::Request; -use leptos::logging::log; +use crate::components::editable_cell::EditableCell; +use crate::components::tag_editor::TagEditor; use leptos::*; use serde::Deserialize; -use std::collections::HashMap; -use wasm_bindgen_futures::spawn_local; +use uuid::Uuid; +use leptos::logging::log; +use crate::models::item::Item; +use std::sync::{Arc, Mutex}; #[derive(Deserialize, Clone, Debug)] -struct WikidataResponse { - entities: HashMap, +struct WikidataSuggestion { + id: String, + label: String, + description: Option, } -#[derive(Deserialize, Clone, Debug)] -struct WikidataEntity { - labels: Option>, - descriptions: Option>, -} - -#[derive(Deserialize, Clone, Debug)] -struct WikidataLabel { - value: String, -} #[component] -pub fn ItemsList(items: ReadSignal>, set_items: WriteSignal>, on_add_item: impl Fn() + 'static, ) -> impl IntoView { - let (wikidata_data, set_wikidata_data) = create_signal(Vec::>::new()); +pub fn ItemsList( + items: ReadSignal>, + set_items: WriteSignal>, +) -> impl IntoView { + let (wikidata_suggestions, set_wikidata_suggestions) = + create_signal(Vec::::new()); - // Fetch data from Wikidata for a given item name - let fetch_wikidata = move |item_name: String| { + // Fetch Wikidata suggestions + let fetch_wikidata_suggestions = move |query: String| { spawn_local(async move { - let url = format!("https://www.wikidata.org/w/api.php?action=wbsearchentities&search={}&language=en&limit=1&format=json", item_name); - match Request::get(&url).send().await { - Ok(response) => match response.json::().await { - Ok(parsed_data) => { - if let Some(entity) = parsed_data.entities.values().next() { - set_wikidata_data.update(|current_data| { - current_data.push(Some(entity.clone())); - }); - } + if query.is_empty() { + set_wikidata_suggestions.set(Vec::new()); + return; + } + + let url = format!( + "https://www.wikidata.org/w/api.php?action=wbsearchentities&search={}&language=en&limit=5&format=json&origin=*", + query + ); + + match gloo_net::http::Request::get(&url).send().await { + Ok(response) => { + if let Ok(data) = response.json::().await { + set_wikidata_suggestions.set(data.search); } - Err(_) => log!("Failed to parse response from Wikidata"), - }, - Err(_) => log!("Failed to make request to Wikidata"), + } + Err(_) => log!("Failed to fetch Wikidata suggestions"), } }); }; - // Handle updating grid cells - let update_item = move |index: usize, column: &str, value: String| { + // Update item fields + let update_item = move |index: usize, field: &str, value: String| { set_items.update(|items| { if let Some(item) = items.get_mut(index) { - match column { - "name" => item.name = value, - "description" => item.description = value, - "tags" => item.tags.push((value.clone(), value)), // For simplicity, adding the same value as key and value. - "review" => item.reviews.push(crate::models::item::ReviewWithRating { - content: value.clone(), - rating: 5, // Assuming a default rating of 5 - }), - "rating" => { - if let Ok(rating) = value.parse::() { - item.reviews.last_mut().map(|r| r.rating = rating); - } + match field { + "name" => { + item.name = value.clone(); + fetch_wikidata_suggestions(value); } + "description" => item.description = value, _ => (), } } }); }; - // Trigger add item event - let add_item = move |_| { - on_add_item(); // Call the passed closure from App to add a new item + // Add a new tag to an item + let add_tag = move |index: usize, key: String, value: String| { + set_items.update(|items| { + if let Some(item) = items.get_mut(index) { + item.tags.push((key, value)); + } + }); + }; + + // Remove a tag from an item + let remove_tag = move |item_index: usize, tag_index: usize| { + set_items.update(|items| { + if let Some(item) = items.get_mut(item_index) { + item.tags.remove(tag_index); + } + }); + }; + + // Add a new item + let add_item = move |_: web_sys::MouseEvent|{ + set_items.update(|items| { + items.push(Item { + id: Uuid::new_v4().to_string(), + name: String::new(), + description: String::new(), + tags: vec![], + reviews:vec![], + wikidata_id: None, + }); + }); + }; + + // Remove an item + let remove_item = move |index: usize| { + set_items.update(|items| { + items.remove(index); + }); }; view! {
-

{ "CompareWare" }

- +

{ "Items List" }

- - - + - {move || { - items.get().iter().enumerate().map(|(index, item)| { - view! { - - - - -
{ "Name" } { "Description" } { "Tags" }{ "Review" }{ "Rating" }{ "Actions" }
- - - - - - -