diff --git a/assets/tailwind.css b/assets/tailwind.css index 6fb3902..0f525e2 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -774,6 +774,12 @@ html { --tw-bg-opacity: 1; background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); } + + .table-zebra tr.hover:hover, + .table-zebra tr.hover:nth-child(even):hover { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); + } } .btn { @@ -1353,6 +1359,11 @@ input.tab:checked + .tab-content, background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); } +.table-zebra tbody tr:nth-child(even) :where(.table-pin-cols tr th) { + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); +} + .btm-nav > *.disabled, .btm-nav > *[disabled] { pointer-events: none; @@ -2075,6 +2086,13 @@ input.tab:checked + .tab-content, background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); } +.table-zebra tr.active, + .table-zebra tr.active:nth-child(even), + .table-zebra-zebra tbody tr:nth-child(even) { + --tw-bg-opacity: 1; + background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); +} + .table :where(thead tr, tbody tr:not(:last-child), tbody tr:first-child:last-child) { border-bottom-width: 1px; --tw-border-opacity: 1; @@ -2389,6 +2407,10 @@ input.tab:checked + .tab-content, width: 100%; } +.max-w-4xl { + max-width: 56rem; +} + .max-w-lg { max-width: 32rem; } @@ -2445,6 +2467,10 @@ input.tab:checked + .tab-content, overflow: auto; } +.overflow-x-auto { + overflow-x: auto; +} + .overflow-y-auto { overflow-y: auto; } @@ -2496,6 +2522,11 @@ input.tab:checked + .tab-content, line-height: 2rem; } +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + .text-xl { font-size: 1.25rem; line-height: 1.75rem; @@ -2523,3 +2554,7 @@ input.tab:checked + .tab-content, .filter { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } + +.hover\:cursor-pointer:hover { + cursor: pointer; +} diff --git a/src/components/admin/members.rs b/src/components/admin/members.rs index a571eaa..fa21ae6 100644 --- a/src/components/admin/members.rs +++ b/src/components/admin/members.rs @@ -1 +1,70 @@ pub mod migration; + +use dioxus::prelude::*; + +use crate::util::model::member::Member; + +pub fn Members() -> Element { + let members = use_resource(fetch_members); + + rsx! { + div { + class: "flex justify-center", + div { + class: "max-w-4xl w-full", + h2 { class: "font-bold text-lg", "Leden" }, + match &*members.read_unchecked() { + Some(Ok(res)) => rsx! { + div { + class: "overflow-x-auto", + table { + class: "table table-zebra table-pin-rows", + thead { + tr { + th { "Relatiecode" } + th { "Naam" } + th { "Registratiecode" } + } + } + tbody { + for member in res { + MemberRow { member: member.clone() } + } + } + } + } + }, + Some(Err(_)) => rsx! { div { "Error while loading members" } }, + None => rsx! { div { "Loading..." } }, + } + } + } + } +} + +#[derive(Props, Clone, PartialEq)] +struct MemberRowProps { + member: Member, +} + +fn MemberRow(props: MemberRowProps) -> Element { + let registration_token = props + .member + .registration_token + .unwrap_or("None".to_string()); + + rsx! { + tr { + class: "hover hover:cursor-pointer", + th { "{props.member.id}" } + td { "{props.member.name.full}" } + td { "{registration_token}" } + } + } +} + +#[server] +async fn fetch_members() -> Result, ServerFnError> { + let members = Member::fetch_all().await?; + Ok(members) +} diff --git a/src/components/layout.rs b/src/components/layout.rs index 70ba716..349c02d 100644 --- a/src/components/layout.rs +++ b/src/components/layout.rs @@ -35,5 +35,6 @@ pub fn Global() -> Element { #[server] async fn get_user_from_cookie() -> Result<(), ServerFnError> { - Err(ServerFnError::new("Not authenticated")) + // Err(ServerFnError::new("Not authenticated")) + Ok(()) } diff --git a/src/components/layout/auth.rs b/src/components/layout/auth.rs index 4d21119..0952a65 100644 --- a/src/components/layout/auth.rs +++ b/src/components/layout/auth.rs @@ -68,7 +68,7 @@ fn Login() -> Element { div { class: "card-actions mt-8", button { - class: "btn btn-wide btn-primary", + class: "btn btn-primary", "Inloggen" } } diff --git a/src/main.rs b/src/main.rs index 1b33c11..8070e3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use tracing::Level; // Use routes use components::admin::members::migration::Migration; +use components::admin::members::Members; use components::agenda::Agenda; use components::home::Home; use components::layout::Global; @@ -25,6 +26,8 @@ pub enum Route { News {}, #[route("/settings")] Settings {}, + #[route("/admin/members")] + Members {}, #[route("/admin/members/migration")] Migration {}, } diff --git a/src/util/model/member/migration.rs b/src/util/model/member/migration.rs index 1507455..9bc1d5f 100644 --- a/src/util/model/member/migration.rs +++ b/src/util/model/member/migration.rs @@ -3,7 +3,7 @@ use crate::util::surrealdb::DB; use super::{Member, MembersMigration}; use once_cell::sync::Lazy; -use std::collections::{BTreeSet, HashMap}; +use std::collections::HashMap; use tokio::sync::Mutex; // Create a store for saving information when migrating to a new members list @@ -168,7 +168,7 @@ impl MembersMigration { hours: new_member_clone.hours, groups: current_member.groups, diploma: new_member_clone.diploma, - registration_token: current_member.diploma, + registration_token: current_member.registration_token, }) } else { // Remove member @@ -178,7 +178,6 @@ impl MembersMigration { for new_member in new_members_list { // Insert new member - // TODO Generate registration token if !current_members_map.contains_key(&new_member.id) { inserted_members.push(new_member); } @@ -229,9 +228,10 @@ impl MembersMigration { for member in members_migration.inserted.clone() { // TODO add hours and diploma support + query = query + format!( - "CREATE member:{} SET name.first = \"{}\", name.full = \"{}\", hours = {:?}, groups = {:?};", + "CREATE member:{} SET name.first = \"{}\", name.full = \"{}\", hours = {:?}, groups = {:?}, registration_token = rand::string(16);", member.id, member.name.first, member.name.full, member.hours, member.groups ) .as_str();