2024-12-09 19:24:46 +03:00
|
|
|
/// Component to display a list of items.
|
2024-12-17 13:06:25 +03:00
|
|
|
/// Iterates through the items and renders their name, description, tags, and reviews.
|
2024-12-06 14:45:14 +03:00
|
|
|
use leptos::*;
|
|
|
|
use crate::models::item::Item;
|
2024-12-18 21:49:26 +03:00
|
|
|
use serde::Deserialize;
|
|
|
|
use futures::future;
|
|
|
|
use gloo_net::http::Request;
|
|
|
|
use wasm_bindgen_futures::spawn_local;
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
// Define the structure for Wikidata API response
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
|
|
struct WikidataResponse {
|
|
|
|
entities: std::collections::HashMap<String, WikidataEntity>,
|
|
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
|
|
struct WikidataEntity {
|
|
|
|
labels: Option<std::collections::HashMap<String, WikidataLabel>>,
|
|
|
|
descriptions: Option<std::collections::HashMap<String, WikidataLabel>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
|
|
struct WikidataLabel {
|
|
|
|
value: String,
|
|
|
|
}
|
2024-12-06 14:45:14 +03:00
|
|
|
|
|
|
|
#[component]
|
2024-12-10 14:49:45 +03:00
|
|
|
pub fn ItemsList(items: ReadSignal<Vec<Item>>) -> impl IntoView {
|
2024-12-17 16:14:49 +03:00
|
|
|
// Create a signal for selected items
|
|
|
|
let (selected_items_signal, set_selected_items) = create_signal(Vec::<usize>::new());
|
2024-12-18 21:49:26 +03:00
|
|
|
let (wikidata_data, set_wikidata_data) = create_signal(Vec::<Option<WikidataEntity>>::new());
|
|
|
|
|
|
|
|
// Fetch additional data from Wikidata for selected items
|
|
|
|
let fetch_wikidata = move || {
|
|
|
|
let selected_indices = selected_items_signal.get();
|
|
|
|
let selected_items: Vec<Item> = selected_indices
|
|
|
|
.iter()
|
|
|
|
.map(|&i| items.get()[i].clone())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
// Wrap `selected_items` in an `Arc` so it can be cloned
|
|
|
|
let selected_items = Arc::new(selected_items);
|
|
|
|
|
|
|
|
// Clone selected_items before the async block to avoid borrowing issues
|
|
|
|
let selected_items_clone: Arc<Vec<Item>> = Arc::clone(&selected_items);
|
2024-12-17 16:14:49 +03:00
|
|
|
|
2024-12-18 21:49:26 +03:00
|
|
|
// For each selected item, fetch Wikidata attributes
|
|
|
|
let futures = selected_items_clone.iter().map(move |item| {
|
|
|
|
let wikidata_id = item.wikidata_id.clone();
|
|
|
|
async move {
|
|
|
|
if let Some(id) = wikidata_id {
|
|
|
|
let url = format!("https://www.wikidata.org/wiki/Special:EntityData/{}.json", id);
|
|
|
|
match Request::get(&url).send().await {
|
|
|
|
Ok(response) => match response.json::<WikidataResponse>().await {
|
|
|
|
Ok(parsed_data) => parsed_data.entities.get(&id).cloned(),
|
|
|
|
Err(_) => None,
|
|
|
|
},
|
|
|
|
Err(_) => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
|
|
spawn_local(async move {
|
|
|
|
let results = future::join_all(futures).await;
|
|
|
|
set_wikidata_data.set(results);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2024-12-18 16:00:40 +03:00
|
|
|
// Function to toggle selection of an item
|
|
|
|
let toggle_selection = move |i: usize| {
|
|
|
|
set_selected_items.update(|items| {
|
|
|
|
if items.contains(&i) {
|
|
|
|
items.retain(|&x| x != i);
|
|
|
|
} else {
|
|
|
|
items.push(i);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2024-12-06 14:45:14 +03:00
|
|
|
view! {
|
|
|
|
<div>
|
|
|
|
<h2>{ "Items" }</h2>
|
|
|
|
<ul>
|
2024-12-17 13:39:41 +03:00
|
|
|
{move || items.get().iter().enumerate().map(|(i, item)| view! {
|
2024-12-06 14:45:14 +03:00
|
|
|
<li key={i.to_string()}>
|
2024-12-17 16:14:49 +03:00
|
|
|
<label>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={selected_items_signal.get().contains(&i)}
|
2024-12-18 16:00:40 +03:00
|
|
|
on:change=move |_| toggle_selection(i)
|
2024-12-17 16:14:49 +03:00
|
|
|
/>
|
|
|
|
{"Select item for comparison"}
|
|
|
|
</label>
|
|
|
|
<div>
|
2024-12-18 16:00:40 +03:00
|
|
|
<strong>{ &item.name }</strong> - { &item.description }
|
2024-12-17 16:14:49 +03:00
|
|
|
</div>
|
2024-12-06 14:45:14 +03:00
|
|
|
<ul>
|
2024-12-17 13:39:41 +03:00
|
|
|
<h4>{ "Tags:" }</h4>
|
2024-12-06 14:45:14 +03:00
|
|
|
{item.tags.iter().map(|(key, value)| view! {
|
2024-12-17 13:39:41 +03:00
|
|
|
<li>{ key.clone() + ": " + value }</li>
|
2024-12-17 13:06:25 +03:00
|
|
|
}).collect::<Vec<_>>()}
|
|
|
|
</ul>
|
|
|
|
<ul>
|
2024-12-17 13:39:41 +03:00
|
|
|
<h4>{ "Reviews:" }</h4>
|
2024-12-17 13:06:25 +03:00
|
|
|
{item.reviews.iter().map(|review| view! {
|
2024-12-17 13:39:41 +03:00
|
|
|
<li>{ format!("Rating: {}/5 - {}", review.rating, review.content) }</li>
|
2024-12-06 14:45:14 +03:00
|
|
|
}).collect::<Vec<_>>()}
|
|
|
|
</ul>
|
|
|
|
</li>
|
2024-12-18 16:00:40 +03:00
|
|
|
}).collect::<Vec<_>>() }
|
2024-12-06 14:45:14 +03:00
|
|
|
</ul>
|
2024-12-17 16:14:49 +03:00
|
|
|
|
2024-12-18 21:49:26 +03:00
|
|
|
// Button to fetch Wikidata attributes
|
|
|
|
<button on:click=move |_| fetch_wikidata()>{ "Fetch External Data" }</button>
|
|
|
|
|
2024-12-17 16:14:49 +03:00
|
|
|
// Comparison Table
|
|
|
|
<h2>{ "Comparison Table" }</h2>
|
|
|
|
<table>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>{ "Item Name" }</th>
|
|
|
|
<th>{ "Description" }</th>
|
|
|
|
<th>{ "Tags" }</th>
|
|
|
|
<th>{ "Reviews" }</th>
|
2024-12-18 21:49:26 +03:00
|
|
|
<th>{ "External Description (Wikidata)" }</th>
|
2024-12-17 16:14:49 +03:00
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
{move || {
|
|
|
|
let selected_indices = selected_items_signal.get();
|
2024-12-18 21:49:26 +03:00
|
|
|
selected_indices.iter().enumerate().map(|(idx, &i)| {
|
2024-12-17 16:14:49 +03:00
|
|
|
let item = &items.get()[i];
|
2024-12-18 21:49:26 +03:00
|
|
|
let wikidata_entity = wikidata_data.get().get(idx).cloned().flatten();
|
2024-12-17 16:14:49 +03:00
|
|
|
view! {
|
|
|
|
<tr key={i.to_string()}>
|
2024-12-18 16:00:40 +03:00
|
|
|
<td>{ &item.name }</td>
|
|
|
|
<td>{ &item.description }</td>
|
2024-12-17 16:14:49 +03:00
|
|
|
<td>
|
|
|
|
{item.tags.iter().map(|(key, value)| view! {
|
|
|
|
<span>{ key.clone() + ": " + value + " " }</span>
|
|
|
|
}).collect::<Vec<_>>()}
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
{item.reviews.iter().map(|review| view! {
|
|
|
|
<span>{ format!("Rating: {}/5 ", review.rating) }</span>
|
|
|
|
}).collect::<Vec<_>>()}
|
|
|
|
</td>
|
2024-12-18 21:49:26 +03:00
|
|
|
<td>
|
|
|
|
{move || {
|
|
|
|
let entity = wikidata_entity.clone(); // Clone the value
|
|
|
|
match entity {
|
|
|
|
Some(entity) => entity
|
|
|
|
.descriptions
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|descriptions| descriptions.get("en"))
|
|
|
|
.map(|label| label.value.clone())
|
|
|
|
.unwrap_or_default(),
|
|
|
|
None => String::from("No description"),
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
</td>
|
2024-12-17 16:14:49 +03:00
|
|
|
</tr>
|
|
|
|
}
|
|
|
|
}).collect::<Vec<_>>()
|
|
|
|
}}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
2024-12-06 14:45:14 +03:00
|
|
|
</div>
|
|
|
|
}
|
2024-12-18 16:00:40 +03:00
|
|
|
}
|