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, ServerFnError> { let user = user::get_auth_session().await?; if user.is_none() { return Err(ServerFnError::::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::::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::::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::::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! {

"Categories"

"Categories are an optional organization method that can be used to group beacons. Beacons can be assigned to multiple categories"

{move || match add_category.value().get() { Some(Ok(_)) => Either::Right(()), None => Either::Right(()), Some(Err(e)) => Either::Left(view! {

"Error creating category:"

{format!("{e:?}")}

}) }} "Add a new beacon category"
"Loading..."

}> { move || match categories.get() { Some(inner) => Either::Right(match inner { Err(e) => Either::Left(view! {

"There was an error loading categories:"

{format!("error: {e:?}")}

}), Ok(cs) => Either::Right(view! { }) }), None => Either::Left(view! {

"Loading..."

}) }}
} } #[component] fn DisplayCategories(categories: Vec) -> 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::::new(); let categories_view = categories .iter() .map(|category| { view! {
  • {category.category_id} ": " {category.category_name.clone()}
  • } }) .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! { {move || match rename_category.value().get() { Some(Ok(_)) => Either::Right(()), None => Either::Right(()), Some(Err(e)) => Either::Left(view! {

    "Failed to rename category:"

    {format!("{e:?}")}

    }) }}
    "Rename category"
    {move || match remove_category.value().get() { Some(Ok(_)) => Either::Right(()), None => Either::Right(()), Some(Err(e)) => Either::Left(view! {

    "Failed to remove category:"

    {format!("{e:?}")}

    }) }}
      {categories_view}
    } }