252 lines
7.8 KiB
Rust
252 lines
7.8 KiB
Rust
use leptos::{either::Either, prelude::*};
|
|
use serde::{Deserialize, Serialize};
|
|
#[cfg(feature = "ssr")]
|
|
use {crate::db::user, leptos::server_fn::error::NoCustomError, sqlx::SqlitePool};
|
|
|
|
use super::BeaconResources;
|
|
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct Category {
|
|
pub category_id: i64,
|
|
pub category_name: String,
|
|
}
|
|
|
|
#[server(prefix = "/api/categories", endpoint = "get")]
|
|
pub async fn get_categories() -> Result<Vec<Category>, ServerFnError> {
|
|
let user = user::get_auth_session().await?;
|
|
|
|
if user.is_none() {
|
|
return Err(ServerFnError::<NoCustomError>::ServerError(
|
|
"You are not signed in!".to_owned(),
|
|
));
|
|
}
|
|
|
|
let db = crate::db::get_db()?;
|
|
|
|
Ok(sqlx::query_as!(Category, "SELECT * FROM beacon_category")
|
|
.fetch_all(&db)
|
|
.await?)
|
|
}
|
|
|
|
#[server(prefix = "/api/categories", endpoint = "add")]
|
|
pub async fn add_category(name: String) -> Result<(), ServerFnError> {
|
|
let user = user::get_auth_session().await?;
|
|
|
|
if user.is_none() {
|
|
return Err(ServerFnError::<NoCustomError>::ServerError(
|
|
"You are not signed in!".to_owned(),
|
|
));
|
|
}
|
|
|
|
let db = crate::db::get_db()?;
|
|
|
|
sqlx::query!(
|
|
"INSERT INTO beacon_category (category_name) VALUES (?)",
|
|
name
|
|
)
|
|
.execute(&db)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[server(prefix = "/api/categories", endpoint = "remove")]
|
|
pub async fn remove_category(id: i64) -> Result<(), ServerFnError> {
|
|
let user = user::get_auth_session().await?;
|
|
|
|
if user.is_none() {
|
|
return Err(ServerFnError::<NoCustomError>::ServerError(
|
|
"You are not signed in!".to_owned(),
|
|
));
|
|
}
|
|
|
|
let db = crate::db::get_db()?;
|
|
|
|
sqlx::query!("DELETE FROM beacon_category WHERE category_id = ?", id)
|
|
.execute(&db)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[server(prefix = "/api/categories", endpoint = "rename")]
|
|
pub async fn rename_category(id: i64, name: String) -> Result<(), ServerFnError> {
|
|
let user = user::get_auth_session().await?;
|
|
|
|
if user.is_none() {
|
|
return Err(ServerFnError::<NoCustomError>::ServerError(
|
|
"You are not signed in!".to_owned(),
|
|
));
|
|
}
|
|
|
|
let db = crate::db::get_db()?;
|
|
|
|
sqlx::query!(
|
|
"UPDATE beacon_category SET category_name = ? WHERE category_id = ?",
|
|
name,
|
|
id
|
|
)
|
|
.execute(&db)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[component]
|
|
pub fn CategoriesView() -> impl IntoView {
|
|
let BeaconResources {
|
|
add_category,
|
|
categories,
|
|
..
|
|
} = expect_context();
|
|
|
|
view! {
|
|
<div class="categories">
|
|
<h2>"Categories"</h2>
|
|
<p>
|
|
"Categories are an optional organization method that can be used to group beacons. Beacons can be assigned to multiple categories"
|
|
</p>
|
|
|
|
<ActionForm action=add_category>
|
|
<fieldset>
|
|
{move || match add_category.value().get() {
|
|
Some(Ok(_)) => Either::Right(()),
|
|
None => Either::Right(()),
|
|
Some(Err(e)) => Either::Left(view! {
|
|
<p>"Error creating category:"</p>
|
|
<p>{format!("{e:?}")}</p>
|
|
})
|
|
}}
|
|
<legend>"Add a new beacon category"</legend>
|
|
<label>"Category name"</label>
|
|
<input name="name"/>
|
|
<div></div>
|
|
<input type="submit" value="Submit" disabled=move||add_category.pending() />
|
|
</fieldset>
|
|
</ActionForm>
|
|
|
|
<Suspense fallback=|| view! { <p>"Loading..."</p> }>
|
|
{ move || match categories.get() {
|
|
Some(inner) => Either::Right(match inner {
|
|
Err(e) => Either::Left(view! {
|
|
<p>"There was an error loading categories:"</p>
|
|
<p>{format!("error: {e:?}")}</p>
|
|
}),
|
|
Ok(cs) => Either::Right(view! {
|
|
<DisplayCategories categories=cs/>
|
|
})
|
|
}),
|
|
None => Either::Left(view! {
|
|
<p>"Loading..."</p>
|
|
})
|
|
}}
|
|
</Suspense>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn DisplayCategories(categories: Vec<Category>) -> impl IntoView {
|
|
let BeaconResources {
|
|
remove_category,
|
|
rename_category,
|
|
..
|
|
} = expect_context();
|
|
|
|
let (target_rename_id, set_target_rename_id) = signal(0);
|
|
let target_rename_name = RwSignal::new("".to_owned());
|
|
|
|
let dialog_ref = NodeRef::<leptos::html::Dialog>::new();
|
|
|
|
let categories_view = categories
|
|
.iter()
|
|
.map(|category| {
|
|
view! {
|
|
<li>
|
|
{category.category_id}
|
|
": "
|
|
{category.category_name.clone()}
|
|
<button
|
|
on:click={
|
|
let id = category.category_id;
|
|
let name = category.category_name.clone();
|
|
let set_target_rename_id = set_target_rename_id.clone();
|
|
let target_rename_name = target_rename_name.clone();
|
|
move |_| {
|
|
set_target_rename_id(id);
|
|
target_rename_name.set(name.clone());
|
|
|
|
if let Some(dialog) = dialog_ref.get() {
|
|
let _ = dialog.show_modal();
|
|
}
|
|
}
|
|
}
|
|
>
|
|
"rename"
|
|
</button>
|
|
<button
|
|
on:click={
|
|
let id = category.category_id;
|
|
move |_| {
|
|
remove_category.dispatch(RemoveCategory { id });
|
|
}
|
|
}
|
|
>
|
|
"delete"
|
|
</button>
|
|
</li>
|
|
}
|
|
})
|
|
.collect_view();
|
|
|
|
Effect::watch(
|
|
move || {
|
|
(
|
|
rename_category.version().get(),
|
|
rename_category.value().get(),
|
|
dialog_ref.get(),
|
|
)
|
|
},
|
|
move |(_, res, dialog_ref), _, _| {
|
|
if let (Some(Ok(())), Some(dialog)) = (res, dialog_ref) {
|
|
let _ = dialog.close();
|
|
}
|
|
},
|
|
false,
|
|
);
|
|
|
|
view! {
|
|
<dialog node_ref=dialog_ref>
|
|
<ActionForm action=rename_category>
|
|
{move || match rename_category.value().get() {
|
|
Some(Ok(_)) => Either::Right(()),
|
|
None => Either::Right(()),
|
|
Some(Err(e)) => Either::Left(view! {
|
|
<p>"Failed to rename category:"</p>
|
|
<p>{format!("{e:?}")}</p>
|
|
})
|
|
}}
|
|
<fieldset>
|
|
<legend>"Rename category"</legend>
|
|
<label>"Name"</label>
|
|
<input name="name" bind:value=target_rename_name />
|
|
<input type="hidden" name="id" value=move||target_rename_id.get() />
|
|
<div></div>
|
|
<input type="submit" value="Submit" disabled=move||rename_category.pending()/>
|
|
</fieldset>
|
|
</ActionForm>
|
|
</dialog>
|
|
{move || match remove_category.value().get() {
|
|
Some(Ok(_)) => Either::Right(()),
|
|
None => Either::Right(()),
|
|
Some(Err(e)) => Either::Left(view! {
|
|
<p>"Failed to remove category:"</p>
|
|
<p>{format!("{e:?}")}</p>
|
|
})
|
|
}}
|
|
<ul>
|
|
{categories_view}
|
|
</ul>
|
|
}
|
|
}
|