User management completed. Leptos upgraded to 0.5.0.
This commit is contained in:
@@ -39,7 +39,9 @@ pub fn data_form<T: 'static + server_fn::ServerFn<()> + Clone + ForValidation>(
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"
|
||||
on:click=move |_| {validator.reset(); opener.hide()}>
|
||||
on:click=move |_| {
|
||||
validator.reset();
|
||||
opener.hide();}>
|
||||
{trl("Close")}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
@@ -50,3 +52,34 @@ pub fn data_form<T: 'static + server_fn::ServerFn<()> + Clone + ForValidation>(
|
||||
</ActionForm>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn question_dialog<T: 'static + server_fn::ServerFn<()> + Clone>(
|
||||
opener: DialogOpener,
|
||||
action: Action<T, Result<ApiResponse<()>, ServerFnError>>,
|
||||
title: &'static str,
|
||||
children: Children
|
||||
) -> impl IntoView {
|
||||
let upd_val = action.value();
|
||||
|
||||
view! {
|
||||
<ActionForm action=action>
|
||||
<ModalDialog opener=opener title=title>
|
||||
<ModalBody>
|
||||
<ServerErr result={upd_val} opener=opener/>
|
||||
{children()}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"
|
||||
on:click=move |_| {
|
||||
opener.hide();}>
|
||||
{trl("No")}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{trl("Yes")}
|
||||
</button>
|
||||
</ModalFooter>
|
||||
</ModalDialog>
|
||||
</ActionForm>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ pub fn Header() -> impl IntoView {
|
||||
<Html
|
||||
lang="cz"
|
||||
dir="ltr"
|
||||
attributes=AdditionalAttributes::from(vec![
|
||||
("data-theme", "theme-default"),
|
||||
("class", "light-style layout-menu-fixed"),
|
||||
("data-template", "vertical-menu-template-free"),
|
||||
("data-assets-path", "/")])
|
||||
attributes=vec![
|
||||
("data-theme", "theme-default".into_attribute()),
|
||||
("class", "light-style layout-menu-fixed".into_attribute()),
|
||||
("data-template", "vertical-menu-template-free".into_attribute()),
|
||||
("data-assets-path", "/".into_attribute())]
|
||||
/>
|
||||
<Title text="Rezervovator"/>
|
||||
<Meta charset="utf-8"/>
|
||||
|
||||
@@ -5,14 +5,27 @@ use leptos::*;
|
||||
pub struct DialogOpener {
|
||||
visible: ReadSignal<bool>,
|
||||
set_visible: WriteSignal<bool>,
|
||||
empty: ReadSignal<String>,
|
||||
set_empty: WriteSignal<String>,
|
||||
not_checked: ReadSignal<Option<String>>,
|
||||
set_not_checked: WriteSignal<Option<String>>,
|
||||
show_err: RwSignal<bool>
|
||||
}
|
||||
|
||||
impl DialogOpener {
|
||||
pub fn new() -> Self {
|
||||
let (visible, set_visible) = create_signal(false);
|
||||
let (empty, set_empty) = create_signal("".to_string());
|
||||
let (not_checked, set_not_checked) = create_signal(None);
|
||||
let show_err = create_rw_signal(false);
|
||||
DialogOpener {
|
||||
visible,
|
||||
set_visible,
|
||||
empty,
|
||||
set_empty,
|
||||
not_checked,
|
||||
set_not_checked,
|
||||
show_err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +39,25 @@ impl DialogOpener {
|
||||
|
||||
pub fn hide(&self) {
|
||||
self.set_visible.update(|state| *state = false);
|
||||
self.set_empty.set("".to_string());
|
||||
self.set_not_checked.set(None);
|
||||
self.show_err.set(false);
|
||||
}
|
||||
|
||||
pub fn empty(&self) -> String {
|
||||
self.empty.get()
|
||||
}
|
||||
|
||||
pub fn not_checked(&self) -> Option<String> {
|
||||
self.not_checked.get()
|
||||
}
|
||||
|
||||
pub fn show_err(&self) -> bool {
|
||||
self.show_err.get()
|
||||
}
|
||||
|
||||
pub fn display_err(&self) {
|
||||
self.show_err.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,15 @@ pub fn ServerErr(
|
||||
if let Some(val) = result.get() {
|
||||
match val {
|
||||
Ok(resp) => if let ApiResponse::Error(err) = resp {
|
||||
opener.display_err();
|
||||
view! {
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert alert-danger" style={move || {
|
||||
if opener.show_err() {
|
||||
""
|
||||
} else {
|
||||
"display: none"
|
||||
}
|
||||
}}>
|
||||
{trl(&err)}
|
||||
</div>
|
||||
}
|
||||
@@ -30,16 +37,6 @@ pub fn ServerErr(
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if let Err(e) = val {
|
||||
view! {
|
||||
<div class="alert alert-danger">
|
||||
"Server error: " {e.to_string()}
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
opener.hide();
|
||||
view! {<div></div>}
|
||||
}*/
|
||||
} else {
|
||||
view! {<div></div>}
|
||||
}
|
||||
|
||||
+81
-45
@@ -1,10 +1,11 @@
|
||||
use leptos::*;
|
||||
use crate::app::MenuHelper;
|
||||
use crate::backend::data::User;
|
||||
use crate::backend::user::{get_user, logout};
|
||||
use crate::components::modal_box::DialogOpener;
|
||||
use crate::locales::trl;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MenuOpener {
|
||||
visible: ReadSignal<bool>,
|
||||
set_visible: WriteSignal<bool>,
|
||||
@@ -23,9 +24,23 @@ impl MenuOpener {
|
||||
self.visible.get()
|
||||
}
|
||||
|
||||
pub fn toggle(&self) {
|
||||
pub fn toggle(self) {
|
||||
let visible = self.visible.get();
|
||||
self.set_visible.update(|v| *v = !visible)
|
||||
self.set_visible.update(|v| *v = !visible);
|
||||
|
||||
let helper = use_context::<MenuHelper>().expect("No menu helper");
|
||||
if !visible {
|
||||
helper.close();
|
||||
helper.set_opened(self);
|
||||
} else {
|
||||
helper.reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.set_visible.set(false);
|
||||
let helper = use_context::<MenuHelper>().expect("No menu helper");
|
||||
helper.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,55 +53,76 @@ pub fn UserMenu(
|
||||
let user = create_resource(move || opener.visible(), move |_| get_user());
|
||||
|
||||
view! {
|
||||
<ul class={move || if opener.visible() {"dropdown-menu dropdown-menu-end show"} else
|
||||
{"dropdown-menu dropdown-menu-end"}}
|
||||
data-bs-popper="none">
|
||||
<ul
|
||||
class=move || {
|
||||
if opener.visible() {
|
||||
"dropdown-menu dropdown-menu-end show"
|
||||
} else {
|
||||
"dropdown-menu dropdown-menu-end"
|
||||
}
|
||||
}
|
||||
data-bs-popper="none"
|
||||
on:mouseleave=move |_| opener.close()
|
||||
>
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" on:click=move |_| {editor.show(); opener.toggle()}>
|
||||
<div class="d-flex">
|
||||
<div class="flex-shrink-0 me-3">
|
||||
<i class="bx bxs-user-account" />
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<Suspense fallback=move || view! {<span>"Loading..."</span>}>
|
||||
{move || {
|
||||
user.get().map(|u| match u {
|
||||
Ok(user) => {
|
||||
let usr = user.unwrap_or(User::default());
|
||||
user_profile.update(|u| *u = usr.clone());
|
||||
view! {
|
||||
<span class="fw-semibold d-block">
|
||||
{usr.full_name.unwrap_or("".to_string())}
|
||||
</span>
|
||||
//<small class="text-muted">"Admin"</small>
|
||||
}},
|
||||
Err(_) => view! {<span>"Error loading user"</span>}
|
||||
})
|
||||
}}
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" on:click=move |_| { editor.show() }>
|
||||
<div class="d-flex">
|
||||
<div class="flex-shrink-0 me-3">
|
||||
<i class="bx bxs-user-account"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<Suspense fallback=move || {
|
||||
view! { <span>"Loading..."</span> }
|
||||
}>
|
||||
{move || {
|
||||
user.get()
|
||||
.map(|u| match u {
|
||||
Ok(user) => {
|
||||
let usr = user.unwrap_or(User::default());
|
||||
user_profile.update(|u| *u = usr.clone());
|
||||
view! {
|
||||
<span class="fw-semibold d-block">
|
||||
{usr.full_name}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
view! {
|
||||
// <small class="text-muted">"Admin"</small>
|
||||
<span>"Error loading user"</span>
|
||||
}
|
||||
}
|
||||
})
|
||||
}}
|
||||
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" on:click=move |_| {pw_dialog.show(); opener.toggle()}>
|
||||
<i class="bx bx-lock me-2"></i>
|
||||
<span class="align-middle">{trl("Change password")}</span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" on:click=move |_| { pw_dialog.show() }>
|
||||
<i class="bx bx-lock me-2"></i>
|
||||
<span class="align-middle">{trl("Change password")}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="dropdown-divider"></div>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="/login" on:click=move |_| {
|
||||
spawn_local(async move {
|
||||
let _ = logout().await;
|
||||
});
|
||||
}>
|
||||
<i class="bx bx-power-off me-2"></i>
|
||||
<span class="align-middle">{trl("Log Out")}</span>
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="/login"
|
||||
on:click=move |_| {
|
||||
spawn_local(async move {
|
||||
let _ = logout().await;
|
||||
});
|
||||
}
|
||||
>
|
||||
<i class="bx bx-power-off me-2"></i>
|
||||
<span class="align-middle">{trl("Log Out")}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user