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:
parent
593bee20a7
commit
08821aaaaf
2 changed files with 90 additions and 29 deletions
|
@ -5,7 +5,9 @@ use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use leptos::logging::log;
|
use leptos::logging::log;
|
||||||
use crate::models::item::Item;
|
use crate::models::item::Item;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
struct WikidataSuggestion {
|
struct WikidataSuggestion {
|
||||||
|
@ -19,6 +21,8 @@ pub fn ItemsList(
|
||||||
items: ReadSignal<Vec<Item>>,
|
items: ReadSignal<Vec<Item>>,
|
||||||
set_items: WriteSignal<Vec<Item>>,
|
set_items: WriteSignal<Vec<Item>>,
|
||||||
) -> impl IntoView {
|
) -> 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
|
// Ensure there's an initial empty row
|
||||||
set_items.set(vec![Item {
|
set_items.set(vec![Item {
|
||||||
|
@ -28,6 +32,7 @@ pub fn ItemsList(
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
reviews: vec![],
|
reviews: vec![],
|
||||||
wikidata_id: None,
|
wikidata_id: None,
|
||||||
|
custom_properties: HashMap::new(),
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
let (wikidata_suggestions, set_wikidata_suggestions) =
|
let (wikidata_suggestions, set_wikidata_suggestions) =
|
||||||
|
@ -69,7 +74,10 @@ pub fn ItemsList(
|
||||||
"description" => {
|
"description" => {
|
||||||
item.description = value.clone();
|
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![],
|
tags: vec![],
|
||||||
reviews: vec![],
|
reviews: vec![],
|
||||||
wikidata_id: None,
|
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
|
// Add a new tag to an item
|
||||||
let add_tag = move |index: usize, key: String, value: String| {
|
let add_tag = move |index: usize, key: String, value: String| {
|
||||||
set_items.update(|items| {
|
set_items.update(|items| {
|
||||||
|
@ -100,7 +118,7 @@ pub fn ItemsList(
|
||||||
let remove_tag = move |item_index: usize, tag_index: usize| {
|
let remove_tag = move |item_index: usize, tag_index: usize| {
|
||||||
set_items.update(|items| {
|
set_items.update(|items| {
|
||||||
if let Some(item) = items.get_mut(item_index) {
|
if let Some(item) = items.get_mut(item_index) {
|
||||||
item.tags.remove(tag_index);
|
item.tags.remove(tag_index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -108,7 +126,7 @@ pub fn ItemsList(
|
||||||
// Remove an item
|
// Remove an item
|
||||||
let remove_item = move |index: usize| {
|
let remove_item = move |index: usize| {
|
||||||
set_items.update(|items| {
|
set_items.update(|items| {
|
||||||
items.remove(index);
|
items.remove(index);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,16 +153,16 @@ pub fn ItemsList(
|
||||||
<tr>
|
<tr>
|
||||||
<td>{ property }</td>
|
<td>{ property }</td>
|
||||||
{move || items.get().iter().enumerate().map(|(index, item)| {
|
{move || items.get().iter().enumerate().map(|(index, item)| {
|
||||||
view! {
|
view! {
|
||||||
<td>
|
<td>
|
||||||
{match property {
|
{match property {
|
||||||
"Name" => view! {
|
"Name" => view! {
|
||||||
<EditableCell
|
<EditableCell
|
||||||
value=item.name.clone()
|
value=item.name.clone()
|
||||||
on_input=move |value| update_item(index, "name", value)
|
on_input=move |value| update_item(index, "name", value)
|
||||||
key=format!("name-{}", index)
|
key=format!("name-{}", index)
|
||||||
/>
|
/>
|
||||||
<ul>
|
<ul>
|
||||||
{move || {
|
{move || {
|
||||||
let suggestions = wikidata_suggestions.get().to_vec();
|
let suggestions = wikidata_suggestions.get().to_vec();
|
||||||
suggestions.into_iter().map(|suggestion| {
|
suggestions.into_iter().map(|suggestion| {
|
||||||
|
@ -160,37 +178,37 @@ pub fn ItemsList(
|
||||||
("wikidata_id".to_string(), id.clone()),
|
("wikidata_id".to_string(), id.clone()),
|
||||||
];
|
];
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<li on:click=move |_| {
|
<li on:click=move |_| {
|
||||||
set_items.update(|items| {
|
set_items.update(|items| {
|
||||||
if let Some(item) = items.get_mut(index) {
|
if let Some(item) = items.get_mut(index) {
|
||||||
item.description = description_for_click.clone();
|
item.description = description_for_click.clone();
|
||||||
item.tags.extend(tags.clone());
|
item.tags.extend(tags.clone());
|
||||||
item.wikidata_id = Some(id.clone());
|
item.wikidata_id = Some(id.clone());
|
||||||
item.name = label_for_click.clone();
|
item.name = label_for_click.clone();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}>
|
}>
|
||||||
{ format!("{} - {}", label_for_display, description_for_display) }
|
{ format!("{} - {}", label_for_display, description_for_display) }
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>()
|
}).collect::<Vec<_>>()
|
||||||
}}
|
}}
|
||||||
</ul>
|
</ul>
|
||||||
}.into_view(),
|
}.into_view(),
|
||||||
"Description" => view! {
|
"Description" => view! {
|
||||||
<EditableCell
|
<EditableCell
|
||||||
value=item.description.clone()
|
value=item.description.clone()
|
||||||
on_input=move |value| update_item(index, "description", value)
|
on_input=move |value| update_item(index, "description", value)
|
||||||
key=format!("description-{}", index)
|
key=format!("description-{}", index)
|
||||||
/>
|
/>
|
||||||
}.into_view(),
|
}.into_view(),
|
||||||
"Tags" => view! {
|
"Tags" => view! {
|
||||||
<TagEditor
|
<TagEditor
|
||||||
tags=item.tags.clone()
|
tags=item.tags.clone()
|
||||||
on_add=move |key, value| add_tag(index, key, value)
|
on_add=move |key, value| add_tag(index, key, value)
|
||||||
on_remove=Arc::new(Mutex::new(move |tag_index: usize| remove_tag(index, tag_index)))
|
on_remove=Arc::new(Mutex::new(move |tag_index: usize| remove_tag(index, tag_index)))
|
||||||
/>
|
/>
|
||||||
}.into_view(),
|
}.into_view(),
|
||||||
"Actions" => view! {
|
"Actions" => view! {
|
||||||
<button on:click=move |_| remove_item(index)>{ "Delete" }</button>
|
<button on:click=move |_| remove_item(index)>{ "Delete" }</button>
|
||||||
|
@ -199,14 +217,56 @@ pub fn ItemsList(
|
||||||
{ "" }
|
{ "" }
|
||||||
}.into_view(),
|
}.into_view(),
|
||||||
}}
|
}}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>()}
|
}).collect::<Vec<_>>()}
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>()}
|
}).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>
|
</tbody>
|
||||||
</table>
|
</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>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// Represents an Item in CompareWare.
|
/// Represents an Item in CompareWare.
|
||||||
/// Each item has metadata and key-value tags for categorization.
|
/// Each item has metadata and key-value tags for categorization.
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
@ -10,6 +10,7 @@ pub struct Item {
|
||||||
pub tags: Vec<(String, String)>,
|
pub tags: Vec<(String, String)>,
|
||||||
pub reviews: Vec<ReviewWithRating>,
|
pub reviews: Vec<ReviewWithRating>,
|
||||||
pub wikidata_id: Option<String>,
|
pub wikidata_id: Option<String>,
|
||||||
|
pub custom_properties: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue