feat(items-list): enable dynamic custom properties

- Edited ItemsList to support dynamic custom properties for each item, managed via HashMap.
- Introduced a UI input for users to add new properties dynamically.
This commit is contained in:
ryan 2025-01-03 14:15:17 +03:00
parent 593bee20a7
commit 08821aaaaf
2 changed files with 90 additions and 29 deletions

View file

@ -5,7 +5,9 @@ use serde::Deserialize;
use uuid::Uuid;
use leptos::logging::log;
use crate::models::item::Item;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use wasm_bindgen::JsCast;
#[derive(Deserialize, Clone, Debug)]
struct WikidataSuggestion {
@ -19,6 +21,8 @@ pub fn ItemsList(
items: ReadSignal<Vec<Item>>,
set_items: WriteSignal<Vec<Item>>,
) -> impl IntoView {
// State to manage dynamic property names
let (custom_properties, set_custom_properties) = create_signal(Vec::<String>::new());
// Ensure there's an initial empty row
set_items.set(vec![Item {
@ -28,6 +32,7 @@ pub fn ItemsList(
tags: vec![],
reviews: vec![],
wikidata_id: None,
custom_properties: HashMap::new(),
}]);
let (wikidata_suggestions, set_wikidata_suggestions) =
@ -69,7 +74,10 @@ pub fn ItemsList(
"description" => {
item.description = value.clone();
}
_ => (),
_ => {
// Update custom property
item.custom_properties.insert(field.to_string(), value.clone());
}
}
}
@ -82,11 +90,21 @@ pub fn ItemsList(
tags: vec![],
reviews: vec![],
wikidata_id: None,
custom_properties: HashMap::new(),
});
}
});
};
// Add a new custom property
let add_property = move |property: String| {
set_custom_properties.update(|props| {
if !props.contains(&property) && !property.is_empty() {
props.push(property);
}
});
};
// Add a new tag to an item
let add_tag = move |index: usize, key: String, value: String| {
set_items.update(|items| {
@ -205,8 +223,50 @@ pub fn ItemsList(
</tr>
}
}).collect::<Vec<_>>()}
// Dynamically adding custom properties as columns
{move || {
let custom_props = custom_properties.get().clone();
custom_props.into_iter().map(move |property| {
let property_clone = property.clone();
view! {
<tr>
<td>{ property }</td>
{move || {
let property_clone = property_clone.clone(); // Clone `property_clone` again for the inner closure
items.get().iter().enumerate().map(move |(index, item)| {
let property_clone_for_closure = property_clone.clone();
view! {
<td>
<EditableCell
value=item.custom_properties.get(&property_clone).cloned().unwrap_or_default()
on_input=move |value| update_item(index, &property_clone_for_closure, value)
key=format!("custom-{}-{}", property_clone, index)
/>
</td>
}
}).collect::<Vec<_>>()
}}
</tr>
}
}).collect::<Vec<_>>()
}}
</tbody>
</table>
<div style="margin-bottom: 20px;">
<input type="text" id="new-property" placeholder="Add New Property"/>
<button on:click=move |_| {
let property = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id("new-property")
.unwrap()
.dyn_into::<web_sys::HtmlInputElement>()
.unwrap()
.value();
add_property(property);
}>{ "Add Property" }</button>
</div>
</div>
}
}

View file

@ -1,7 +1,7 @@
/// Represents an Item in CompareWare.
/// Each item has metadata and key-value tags for categorization.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Item {
pub id: String,
@ -10,6 +10,7 @@ pub struct Item {
pub tags: Vec<(String, String)>,
pub reviews: Vec<ReviewWithRating>,
pub wikidata_id: Option<String>,
pub custom_properties: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]