diff --git a/assets/tailwind.css b/assets/tailwind.css index 8fcef68..4122385 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -2840,6 +2840,20 @@ details.collapse summary::-webkit-details-marker { 0 0 0 2px var(--tglbg) inset; } +.toggle-primary:focus-visible { + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.toggle-primary:checked, + .toggle-primary[aria-checked="true"] { + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + --tw-border-opacity: 0.1; + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + .toggle:disabled { cursor: not-allowed; --tw-border-opacity: 1; diff --git a/src/components/admin/users.rs b/src/components/admin/users.rs index c811eb1..f17a698 100644 --- a/src/components/admin/users.rs +++ b/src/components/admin/users.rs @@ -4,6 +4,7 @@ use crate::util::model::{session::Session, user::User}; pub fn Users() -> Element { let users = use_resource(fetch_users); + let modal_user: Signal> = use_signal(|| None); rsx! { div { @@ -23,7 +24,7 @@ pub fn Users() -> Element { } tbody { for user in res { - UserRow { user: user.clone() } + UserRow { user: user.clone(), modal_user: modal_user } } } } @@ -32,25 +33,136 @@ pub fn Users() -> Element { Some(Err(_)) => rsx! { div { "Error while loading users" } }, None => rsx! { div { "Loading..." } }, } + UserModal { user: modal_user } } } } -#[derive(Props, Clone, PartialEq)] -struct UserRowProps { - user: User, -} - -fn UserRow(props: UserRowProps) -> Element { +#[component] +fn UserRow(user: User, modal_user: Signal>) -> Element { rsx! { tr { class: "hover hover:cursor-pointer", - th { "{props.user.id}" } - td { "{props.user.email}" } + onclick: move |_| { + modal_user.set(Some(user.to_owned())) + }, + th { "{user.id}" } + td { "{user.email}" } } } } +#[component] +fn UserModal(user: Signal>) -> Element { + let mut loading = use_signal(|| false); + let mut user_admin = use_signal(|| false); + + use_effect(move || { + if let Some(u) = user() { + user_admin.set(u.admin); + } + }); + + let submit = move |_| async move { + loading.set(true); + if let Some(u) = user() { + if let Ok(_res) = update_user(u.id, user_admin()).await {}; + }; + loading.set(false); + user.set(None) + }; + + rsx! { + dialog { + class: "modal modal-bottom sm:modal-middle z-50", + open: user().is_some(), + div { + class: "modal-box", + h3 { class: "text-lg font-bold", "Gebruiker bewerken" }, + if let Some(u) = user() { + UserView { user: u, admin: user_admin } + } else { + "Geen gebruiker geselecteerd" + } + div { + class: "modal-action", + button { + class: "btn", + onclick: move |_| user.set(None), + disabled: loading(), + if loading() { + span { class: "loading loading-spinner" } + }, + "Annuleren" + } + button { + class: "btn btn-primary", + onclick: submit, + disabled: loading(), + if loading() { + span { class: "loading loading-spinner" } + }, + "Bewerken" + } + } + } + label { + class: "modal-backdrop bg-black/50", + onclick: move |_| user.set(None) + } + } + } +} + +#[component] +fn UserView(user: User, admin: Signal) -> Element { + rsx! { + div { + div { + class: "grid gap-3", + label { + class: "form-control w-full", + div { + class: "label", + span { class: "label-text", "Id" } + } + b { class: "select-all", "{user.id}" }, + } + label { + class: "form-control w-full", + div { + class: "label", + span { class: "label-text", "Email" } + } + b { class: "select-all", "{user.email}" }, + } + } + div { + class: "mt-5", + label { + class: "form-control", + label { + class: "label cursor-pointer justify-start gap-2", + input { + r#type: "checkbox", + class: "toggle toggle-primary", + checked: admin(), + oninput: move |event| { + if event.value() == "true".to_string() { + admin.set(true); + } else { + admin.set(false); + } + } + }, + span { class: "label-text", "Admin" }, + } + } + } + } + } +} + #[server] async fn fetch_users() -> Result, ServerFnError> { let user = Session::fetch_current_user().await?; @@ -63,3 +175,16 @@ async fn fetch_users() -> Result, ServerFnError> { Ok(users) } + +#[server] +async fn update_user(id: String, admin: bool) -> Result<(), ServerFnError> { + let user = Session::fetch_current_user().await?; + + if !user.admin { + return Err(crate::Error::NoPermissions.into()); + } + + User::update_with_id(&id, admin).await?; + + Ok(()) +} diff --git a/src/util/model/user.rs b/src/util/model/user.rs index dc55c64..d38d762 100644 --- a/src/util/model/user.rs +++ b/src/util/model/user.rs @@ -162,4 +162,16 @@ impl User { Ok(()) } + + pub async fn update_with_id(id: &str, admin: bool) -> Result<(), crate::Error> { + let res = DB + .query("UPDATE $user SET admin = $admin;") + .bind(("user", Thing::from(("user", id)))) + .bind(("admin", admin)) + .await?; + + res.check()?; + + Ok(()) + } }