Added members management system

This commit is contained in:
xeovalyte 2024-10-03 15:59:31 +02:00
parent aa1063a344
commit a53ad775dc
Signed by: xeovalyte
SSH Key Fingerprint: SHA256:kSQDrQDmKzljJzfGYcd3m9RqHi4h8rSwkZ3sQ9kBURo
6 changed files with 151 additions and 32 deletions

View File

@ -2927,6 +2927,10 @@ details.collapse summary::-webkit-details-marker {
margin-left: auto;
}
.mr-3 {
margin-right: 0.75rem;
}
.mt-10 {
margin-top: 2.5rem;
}
@ -3180,6 +3184,11 @@ details.collapse summary::-webkit-details-marker {
font-weight: 400;
}
.text-error {
--tw-text-opacity: 1;
color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)));
}
.text-primary {
--tw-text-opacity: 1;
color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)));

View File

@ -235,8 +235,5 @@ async fn register(
async fn fetch_member(registration_token: String) -> Result<Member, ServerFnError> {
let member = Member::fetch_from_registration_token(registration_token).await?;
match member {
Some(m) => Ok(m),
None => Err(ServerFnError::new("Lid niet gevonden")),
}
Ok(member)
}

View File

@ -121,3 +121,22 @@ pub fn XMark() -> Element {
}
}
}
#[component]
pub fn Trash() -> Element {
rsx! {
svg {
"fill": "none",
"stroke": "currentColor",
"xmlns": "http://www.w3.org/2000/svg",
"stroke-width": "1.5",
"viewBox": "0 0 24 24",
class: "size-6",
path {
"stroke-linecap": "round",
"stroke-linejoin": "round",
"d": "m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
}
}
}
}

View File

@ -1,9 +1,8 @@
use dioxus::prelude::*;
use crate::components::layout::icons;
use crate::util::model::{member::Member, session::Session, user::User};
use super::admin::members;
#[component]
pub fn Settings() -> Element {
rsx! {
@ -216,9 +215,10 @@ async fn change_password(old_password: String, new_password: String) -> Result<(
}
fn Members() -> Element {
let members = use_resource(fetch_members);
let mut members = use_resource(fetch_members);
let mut is_open = use_signal(|| false);
let mut input_registration_token = use_signal(|| "".to_string());
rsx! {
div {
@ -233,25 +233,71 @@ fn Members() -> Element {
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" }
match &*members.read() {
Some(Ok(res)) => {
let members_state = res.to_owned();
rsx! {
for member in members_state {
div {
class: "flex items-center",
div {
class: "flex flex-col",
div {
class: "",
span { class: "font-bold", "{member.name.full}", }
span { class: "mx-1.5", "" }
span { "{member.id}" }
}
div {
class: "",
span { "LS2" },
span { class: "mx-1.5", "" }
span { "A1, B2" }
}
},
div {
class: "ml-auto mr-3 h-full text-error hover:cursor-pointer",
onclick: move |_| {
let id = member.id.clone();
async move {
if let Ok(_res) = delete_member_relation(id).await {
members.restart();
}
}
},
icons::Trash {}
}
}
div { class: "divider my-0" }
}
div {
class: "w-full join",
input {
r#type: "text",
class: "input input-bordered w-full join-item",
value: "{input_registration_token}",
oninput: move |event| input_registration_token.set(event.value())
},
button {
class: "btn join-item btn-neutral",
onclick: move |_| async move {
if let Ok(_res) = relate_member(input_registration_token()).await {
members.restart();
input_registration_token.set("".to_string());
}
},
icons::UserPlus {},
"Toevoegen",
}
}
}
div {
class: "",
span { "LS2" },
span { class: "mx-1.5", "" }
span { "A1, B2" }
}
}
},
Some(Err(_)) => rsx! {
"Error"
},
None => rsx! { div { "Loading..." } },
}
div { class: "divider my-0" }
}
},
},
@ -266,3 +312,20 @@ async fn fetch_members() -> Result<Vec<Member>, ServerFnError> {
Ok(members)
}
#[server]
async fn relate_member(registration_token: String) -> Result<(), ServerFnError> {
let user = Session::fetch_current_user().await?;
let member_id = Member::fetch_from_registration_token(registration_token).await?;
user.relate_member(member_id.id).await?;
Ok(())
}
#[server]
async fn delete_member_relation(member_id: String) -> Result<(), ServerFnError> {
let user = Session::fetch_current_user().await?;
user.remove_relation(member_id).await?;
Ok(())
}

View File

@ -41,15 +41,16 @@ impl Member {
pub async fn fetch_from_registration_token(
registration_token: String,
) -> Result<Option<Self>, surrealdb::Error> {
) -> Result<Self, crate::Error> {
let mut res = DB
.query("SELECT 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?;
let member: Option<Self> = res.take(0)?;
Ok(member)
match res.take(0)? {
Some(m) => Ok(m),
None => Err(crate::Error::NoDocument),
}
}
pub async fn fetch_from_user(user_id: String) -> Result<Vec<Self>, crate::Error> {
@ -60,8 +61,6 @@ impl Member {
.bind(("user_id", user_id))
.await?;
tracing::info!("{res:?}");
let members: Vec<Self> = res.take(0)?;
Ok(members)

View File

@ -45,6 +45,40 @@ impl User {
}
}
pub async fn relate_member(&self, member_id: String) -> Result<(), crate::Error> {
let mut res = DB
.query("
IF (SELECT * FROM ONLY user_to_member WHERE in = type::thing('user', $user_id) AND out = type::thing('member', $member_id) LIMIT 1) == NONE {
RELATE ONLY (type::thing('user', $user_id))->user_to_member->(type::thing('member', $member_id));
} ELSE {
THROW 'Relation already exists'
};
")
.bind(("user_id", self.id.to_string()))
.bind(("member_id", member_id))
.await?;
if res.take_errors().len() != 0 {
return Err(crate::Error::NoDocument);
}
Ok(())
}
pub async fn remove_relation(&self, member_id: String) -> Result<(), crate::Error> {
let mut res = DB
.query("DELETE type::thing('user', $user_id)->user_to_member WHERE out = type::thing('member', $member_id)")
.bind(("user_id", self.id.clone()))
.bind(("member_id", member_id))
.await?;
if res.take_errors().len() != 0 {
return Err(crate::Error::NoDocument);
}
Ok(())
}
pub async fn verify_credentials(
email: String,
password: String,
@ -99,8 +133,6 @@ impl User {
.bind(("new_password", new_password))
.await?;
tracing::info!("{res:?}");
if res.take_errors().len() != 0 {
return Err(crate::Error::NoDocument);
}