Compare commits
2 Commits
062fbf6bbc
...
aa1063a344
Author | SHA1 | Date | |
---|---|---|---|
aa1063a344 | |||
24008ed668 |
@ -1007,6 +1007,27 @@ html {
|
|||||||
min-height: fit-content;
|
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 {
|
.dropdown {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -2039,6 +2060,10 @@ details.collapse summary::-webkit-details-marker {
|
|||||||
content: "−";
|
content: "−";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.divider:not(:empty) {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown.dropdown-open .dropdown-content,
|
.dropdown.dropdown-open .dropdown-content,
|
||||||
.dropdown:focus .dropdown-content,
|
.dropdown:focus .dropdown-content,
|
||||||
.dropdown:focus-within .dropdown-content {
|
.dropdown:focus-within .dropdown-content {
|
||||||
@ -2866,11 +2891,26 @@ details.collapse summary::-webkit-details-marker {
|
|||||||
position: static;
|
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 {
|
.mx-auto {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.my-0 {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.mb-1 {
|
.mb-1 {
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use dioxus::prelude::*;
|
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]
|
#[component]
|
||||||
pub fn Settings() -> Element {
|
pub fn Settings() -> Element {
|
||||||
@ -9,6 +11,7 @@ pub fn Settings() -> Element {
|
|||||||
class: "w-full max-w-2xl space-y-3",
|
class: "w-full max-w-2xl space-y-3",
|
||||||
Account {},
|
Account {},
|
||||||
Password {},
|
Password {},
|
||||||
|
Members {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,10 +23,14 @@ fn Account() -> Element {
|
|||||||
let mut is_open = use_signal(|| false);
|
let mut is_open = use_signal(|| false);
|
||||||
let mut input_email = use_signal(|| user.email);
|
let mut input_email = use_signal(|| user.email);
|
||||||
|
|
||||||
|
let mut loading = use_signal(|| false);
|
||||||
|
|
||||||
let submit = move |_| async move {
|
let submit = move |_| async move {
|
||||||
|
loading.set(true);
|
||||||
if let Ok(_) = change_email(input_email()).await {
|
if let Ok(_) = change_email(input_email()).await {
|
||||||
tracing::info!("User email changed");
|
tracing::info!("User email changed");
|
||||||
};
|
};
|
||||||
|
loading.set(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
@ -65,16 +72,26 @@ fn Account() -> Element {
|
|||||||
button {
|
button {
|
||||||
class: "btn btn-outline btn-error",
|
class: "btn btn-outline btn-error",
|
||||||
onclick: move |_| async move {
|
onclick: move |_| async move {
|
||||||
|
loading.set(true);
|
||||||
if let Ok(_) = logout().await {
|
if let Ok(_) = logout().await {
|
||||||
let window = web_sys::window().expect("Could not find window");
|
let window = web_sys::window().expect("Could not find window");
|
||||||
window.location().reload().expect("Could not reload window");
|
window.location().reload().expect("Could not reload window");
|
||||||
}
|
}
|
||||||
|
loading.set(false);
|
||||||
},
|
},
|
||||||
|
disabled: loading(),
|
||||||
|
if loading() {
|
||||||
|
span { class: "loading loading-spinner" }
|
||||||
|
}
|
||||||
"Uitloggen",
|
"Uitloggen",
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
class: "ml-auto btn btn-primary",
|
class: "ml-auto btn btn-primary",
|
||||||
onclick: submit,
|
onclick: submit,
|
||||||
|
disabled: loading(),
|
||||||
|
if loading() {
|
||||||
|
span { class: "loading loading-spinner" }
|
||||||
|
}
|
||||||
"Opslaan",
|
"Opslaan",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,10 +126,14 @@ fn Password() -> Element {
|
|||||||
let mut input_new_password = use_signal(|| "".to_string());
|
let mut input_new_password = use_signal(|| "".to_string());
|
||||||
let mut input_new_password_repeat = 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 {
|
let submit = move |_| async move {
|
||||||
|
loading.set(true);
|
||||||
if let Ok(_) = change_password(input_old_password(), input_new_password()).await {
|
if let Ok(_) = change_password(input_old_password(), input_new_password()).await {
|
||||||
tracing::info!("User password changed");
|
tracing::info!("User password changed");
|
||||||
};
|
};
|
||||||
|
loading.set(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
@ -172,6 +193,10 @@ fn Password() -> Element {
|
|||||||
button {
|
button {
|
||||||
class: "ml-auto btn btn-primary",
|
class: "ml-auto btn btn-primary",
|
||||||
onclick: submit,
|
onclick: submit,
|
||||||
|
disabled: loading(),
|
||||||
|
if loading() {
|
||||||
|
span { class: "loading loading-spinner" }
|
||||||
|
}
|
||||||
"Opslaan",
|
"Opslaan",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,3 +214,55 @@ async fn change_password(old_password: String, new_password: String) -> Result<(
|
|||||||
|
|
||||||
Ok(())
|
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<Vec<Member>, ServerFnError> {
|
||||||
|
let user = Session::fetch_current_user().await?;
|
||||||
|
|
||||||
|
let members = Member::fetch_from_user(user.id).await?;
|
||||||
|
|
||||||
|
Ok(members)
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use crate::util::surrealdb::DB;
|
use crate::util::surrealdb::{thing_to_string, DB};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
@ -7,6 +7,7 @@ mod migration;
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
||||||
pub struct Member {
|
pub struct Member {
|
||||||
|
#[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub hours: Vec<String>,
|
pub hours: Vec<String>,
|
||||||
@ -31,7 +32,7 @@ pub struct MembersMigration {
|
|||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
impl Member {
|
impl Member {
|
||||||
pub async fn fetch_all() -> Result<Vec<Self>, surrealdb::Error> {
|
pub async fn fetch_all() -> Result<Vec<Self>, surrealdb::Error> {
|
||||||
let mut res = DB.query("SELECT record::id(id) as id, name.first, name.full, hours, groups, diploma, registration_token FROM member").await?;
|
let mut res = DB.query("SELECT id, name.first, name.full, hours, groups, diploma, registration_token FROM member").await?;
|
||||||
|
|
||||||
let members: Vec<Self> = res.take(0)?;
|
let members: Vec<Self> = res.take(0)?;
|
||||||
|
|
||||||
@ -41,10 +42,8 @@ impl Member {
|
|||||||
pub async fn fetch_from_registration_token(
|
pub async fn fetch_from_registration_token(
|
||||||
registration_token: String,
|
registration_token: String,
|
||||||
) -> Result<Option<Self>, surrealdb::Error> {
|
) -> Result<Option<Self>, 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
|
let mut res = DB
|
||||||
.query(query)
|
.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))
|
.bind(("registration_token", registration_token))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -52,6 +51,21 @@ impl Member {
|
|||||||
|
|
||||||
Ok(member)
|
Ok(member)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_from_user(user_id: String) -> Result<Vec<Self>, crate::Error> {
|
||||||
|
let mut res = DB
|
||||||
|
.query(
|
||||||
|
"SELECT VALUE ->user_to_member->member.* FROM ONLY type::thing('user', $user_id)",
|
||||||
|
)
|
||||||
|
.bind(("user_id", user_id))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
tracing::info!("{res:?}");
|
||||||
|
|
||||||
|
let members: Vec<Self> = res.take(0)?;
|
||||||
|
|
||||||
|
Ok(members)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MembersMigration {
|
impl MembersMigration {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use crate::util::surrealdb::DB;
|
use crate::util::surrealdb::{thing_to_string, DB};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -8,7 +8,9 @@ use super::user::User;
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
|
#[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))]
|
||||||
id: String,
|
id: String,
|
||||||
|
#[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))]
|
||||||
user_id: String,
|
user_id: String,
|
||||||
expires: i64,
|
expires: i64,
|
||||||
token: String,
|
token: String,
|
||||||
@ -18,7 +20,7 @@ pub struct Session {
|
|||||||
impl Session {
|
impl Session {
|
||||||
pub async fn new(user_id: String) -> Result<Self, crate::Error> {
|
pub async fn new(user_id: String) -> Result<Self, crate::Error> {
|
||||||
let mut res = DB
|
let mut res = DB
|
||||||
.query("CREATE ONLY session SET user = type::thing('user', $user_id) RETURN record::id(id) as id, record::id(user) as user_id, time::unix(expires) as expires, token;")
|
.query("CREATE ONLY session SET user = type::thing('user', $user_id) RETURN id, user as user_id, time::unix(expires) as expires, token;")
|
||||||
.bind(("user_id", user_id))
|
.bind(("user_id", user_id))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -59,7 +61,9 @@ impl Session {
|
|||||||
|
|
||||||
pub async fn fetch_user_from_token(session_token: String) -> Result<User, crate::Error> {
|
pub async fn fetch_user_from_token(session_token: String) -> Result<User, crate::Error> {
|
||||||
let mut res = DB
|
let mut res = DB
|
||||||
.query("SELECT record::id(user) as id, user.email as email FROM session WHERE token = $session_token")
|
.query(
|
||||||
|
"SELECT user as id, user.email as email FROM session WHERE token = $session_token",
|
||||||
|
)
|
||||||
.bind(("session_token", session_token))
|
.bind(("session_token", session_token))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use crate::util::surrealdb::DB;
|
use crate::util::surrealdb::{thing_to_string, DB};
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use surrealdb::sql::statements::{BeginStatement, CommitStatement};
|
use surrealdb::sql::statements::{BeginStatement, CommitStatement};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
#[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
@ -21,7 +22,7 @@ impl User {
|
|||||||
) -> Result<Self, crate::Error> {
|
) -> Result<Self, crate::Error> {
|
||||||
// Create user record
|
// Create user record
|
||||||
let mut transaction = DB.query(BeginStatement::default())
|
let mut transaction = DB.query(BeginStatement::default())
|
||||||
.query("let $user = CREATE ONLY user SET email = $email, password = crypto::argon2::generate($password) RETURN record::id(id) as id, email;")
|
.query("let $user = CREATE ONLY user SET email = $email, password = crypto::argon2::generate($password) RETURN id, email;")
|
||||||
.bind(("email", email))
|
.bind(("email", email))
|
||||||
.bind(("password", password));
|
.bind(("password", password));
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ impl User {
|
|||||||
password: String,
|
password: String,
|
||||||
) -> Result<String, crate::Error> {
|
) -> Result<String, crate::Error> {
|
||||||
let mut res = DB
|
let mut res = DB
|
||||||
.query("SELECT VALUE record::id(id) as id FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)")
|
.query("SELECT VALUE id FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)")
|
||||||
.bind(("email", email)).bind(("password", password))
|
.bind(("email", email)).bind(("password", password))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
use surrealdb::engine::local::{Db, RocksDb};
|
use surrealdb::engine::local::{Db, RocksDb};
|
||||||
|
use surrealdb::sql::Thing;
|
||||||
use surrealdb::Surreal;
|
use surrealdb::Surreal;
|
||||||
|
|
||||||
pub static DB: Lazy<Surreal<Db>> = Lazy::new(|| Surreal::init());
|
pub static DB: Lazy<Surreal<Db>> = Lazy::new(|| Surreal::init());
|
||||||
|
|
||||||
|
pub fn thing_to_string<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let t = Thing::deserialize(deserializer)?;
|
||||||
|
Ok(t.id.to_raw())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn initialize() -> surrealdb::Result<()> {
|
pub async fn initialize() -> surrealdb::Result<()> {
|
||||||
DB.connect::<RocksDb>("./database").await?;
|
DB.connect::<RocksDb>("./database").await?;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user