diff --git a/Cargo.toml b/Cargo.toml index d31366b..2ef176b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ axum = { version = "0.7", optional = true } axum-extra = { version = "0.9", features = ["cookie"], optional = true } time = { version = "0.3", optional = true } once_cell = { version = "1.19", optional = true } -surrealdb = { version = "2.0", features = ["kv-rocksdb"], optional = true } +surrealdb = { version = "2.0", features = [], optional = true } thiserror = { version = "1.0" } csv = { version = "1.3", optional = true } @@ -29,5 +29,5 @@ manganis = "0.2" [features] default = [] -server = [ "dioxus/axum", "tokio", "axum", "axum-extra", "time", "once_cell", "surrealdb", "csv" ] -web = ["dioxus/web"] +server = [ "dioxus/axum", "tokio", "axum", "axum-extra", "time", "once_cell", "surrealdb/kv-rocksdb", "csv" ] +web = ["dioxus/web", "surrealdb"] diff --git a/assets/tailwind.css b/assets/tailwind.css index d4e1986..1963d74 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -1007,6 +1007,27 @@ html { min-height: fit-content; } +.divider { + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; + margin-top: 1rem; + margin-bottom: 1rem; + height: 1rem; + white-space: nowrap; +} + +.divider:before, + .divider:after { + height: 0.125rem; + width: 100%; + flex-grow: 1; + --tw-content: ''; + content: var(--tw-content); + background-color: var(--fallback-bc,oklch(var(--bc)/0.1)); +} + .dropdown { position: relative; display: inline-block; @@ -2039,6 +2060,10 @@ details.collapse summary::-webkit-details-marker { content: "−"; } +.divider:not(:empty) { + gap: 1rem; +} + .dropdown.dropdown-open .dropdown-content, .dropdown:focus .dropdown-content, .dropdown:focus-within .dropdown-content { @@ -2866,11 +2891,26 @@ details.collapse summary::-webkit-details-marker { position: static; } +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} + +.mx-1\.5 { + margin-left: 0.375rem; + margin-right: 0.375rem; +} + .mx-auto { margin-left: auto; margin-right: auto; } +.my-0 { + margin-top: 0px; + margin-bottom: 0px; +} + .mb-1 { margin-bottom: 0.25rem; } diff --git a/src/components/settings.rs b/src/components/settings.rs index bc834fd..28b75c5 100644 --- a/src/components/settings.rs +++ b/src/components/settings.rs @@ -1,6 +1,8 @@ use dioxus::prelude::*; -use crate::util::model::{session::Session, user::User}; +use crate::util::model::{member::Member, session::Session, user::User}; + +use super::admin::members; #[component] pub fn Settings() -> Element { @@ -9,6 +11,7 @@ pub fn Settings() -> Element { class: "w-full max-w-2xl space-y-3", Account {}, Password {}, + Members {}, } } } @@ -20,10 +23,14 @@ fn Account() -> Element { let mut is_open = use_signal(|| false); let mut input_email = use_signal(|| user.email); + let mut loading = use_signal(|| false); + let submit = move |_| async move { + loading.set(true); if let Ok(_) = change_email(input_email()).await { tracing::info!("User email changed"); }; + loading.set(false); }; rsx! { @@ -65,16 +72,26 @@ fn Account() -> Element { button { class: "btn btn-outline btn-error", onclick: move |_| async move { + loading.set(true); if let Ok(_) = logout().await { let window = web_sys::window().expect("Could not find window"); window.location().reload().expect("Could not reload window"); } + loading.set(false); }, + disabled: loading(), + if loading() { + span { class: "loading loading-spinner" } + } "Uitloggen", } button { class: "ml-auto btn btn-primary", onclick: submit, + disabled: loading(), + if loading() { + span { class: "loading loading-spinner" } + } "Opslaan", } } @@ -109,10 +126,14 @@ fn Password() -> Element { let mut input_new_password = use_signal(|| "".to_string()); let mut input_new_password_repeat = use_signal(|| "".to_string()); + let mut loading = use_signal(|| false); + let submit = move |_| async move { + loading.set(true); if let Ok(_) = change_password(input_old_password(), input_new_password()).await { tracing::info!("User password changed"); }; + loading.set(false); }; rsx! { @@ -172,6 +193,10 @@ fn Password() -> Element { button { class: "ml-auto btn btn-primary", onclick: submit, + disabled: loading(), + if loading() { + span { class: "loading loading-spinner" } + } "Opslaan", } } @@ -189,3 +214,55 @@ async fn change_password(old_password: String, new_password: String) -> Result<( Ok(()) } + +fn Members() -> Element { + let members = use_resource(fetch_members); + + let mut is_open = use_signal(|| false); + + rsx! { + div { + class: "collapse collapse-arrow bg-base-200", + class: if is_open() { "collapse-open" }, + div { + class: "collapse-title text-lg font-medium hover:cursor-pointer select-none", + onclick: move |_| is_open.toggle(), + "Leden", + }, + div { + class: "collapse-content", + div { + class: "pl-2 flex flex-col gap-3", + div { + class: "flex", + div { + class: "flex flex-col", + div { + class: "", + span { class: "font-bold", "Timo Boomers", } + span { class: "mx-1.5", "•" } + span { "123" } + } + div { + class: "", + span { "LS2" }, + span { class: "mx-1.5", "•" } + span { "A1, B2" } + } + } + } + div { class: "divider my-0" } + } + }, + }, + } +} + +#[server] +async fn fetch_members() -> Result, ServerFnError> { + let user = Session::fetch_current_user().await?; + + let members = Member::fetch_from_user(user.id).await?; + + Ok(members) +} diff --git a/src/util/model/member.rs b/src/util/model/member.rs index 95fede8..3f184b9 100644 --- a/src/util/model/member.rs +++ b/src/util/model/member.rs @@ -41,10 +41,8 @@ impl Member { pub async fn fetch_from_registration_token( registration_token: String, ) -> Result, surrealdb::Error> { - let query = format!("SELECT record::id(id) as id, name.first, name.full, hours, groups, diploma, registration_token FROM ONLY member WHERE registration_token = $registration_token LIMIT 1"); - let mut res = DB - .query(query) + .query("SELECT record::id(id) as id, name.first, name.full, hours, groups, diploma, registration_token FROM ONLY member WHERE registration_token = $registration_token LIMIT 1") .bind(("registration_token", registration_token)) .await?; @@ -52,6 +50,21 @@ impl Member { Ok(member) } + + pub async fn fetch_from_user(user_id: String) -> Result, crate::Error> { + let mut res = DB + .query( + "SELECT VALUE array::map(->user_to_member->member.*, |$obj| {{ id: record::id($obj.id), }}) FROM ONLY type::thing('user', $user_id)", + ) + .bind(("user_id", user_id)) + .await?; + + tracing::info!("{res:?}"); + + let members: Vec = res.take(0)?; + + Ok(members) + } } impl MembersMigration {