Implemented admin overview.

This commit is contained in:
2024-02-05 12:00:38 +01:00
parent 6c7fd2e46f
commit e69db7f40b
9 changed files with 402 additions and 74 deletions
+12 -57
View File
@@ -1,69 +1,24 @@
use leptos::*;
use crate::components::modal_box::{DialogOpener, ModalDialog, ModalBody, ModalFooter};
use crate::components::modal_box::DialogOpener;
use crate::locales::trl;
use crate::pages::new_reservations::NewReservations;
use crate::pages::today_reservations::NextReservations;
/// Renders the home page of your application.
#[component]
pub fn HomePage() -> impl IntoView {
// Creates a reactive value to update the button
let (count, set_count) = create_signal(0);
let on_click = move |_| set_count.update(|count| *count += 1);
let dialog = DialogOpener::new();
//let (dialog, set_dialog) = create_signal(false);
//let on_dialog = move |_| dialog.set_visible.update(|dialog| {*dialog = true});
//let pok = use_context::<Request>();
//log!("{:?}", pok);
let app_opener = DialogOpener::new();
let cancel_opener = DialogOpener::new();
view! {
<ModalDialog opener={dialog} title="Titulek">
<ModalBody>
<div class="row">
<div class="col mb-3">
<label for="nameWithTitle" class="form-label">"Name"</label>
<input
type="text"
id="nameWithTitle"
class="form-control"
placeholder="Enter Name"
/>
</div>
<h1>{trl("Overview")}</h1>
<div class="row mb-5">
<div class="col-md">
<NewReservations app_opener=app_opener cancel_opener=cancel_opener/>
</div>
<div class="row g-2">
<div class="col mb-0">
<label for="emailWithTitle" class="form-label">"Email"</label>
<input
type="text"
id="emailWithTitle"
class="form-control"
placeholder="xxxx@xxx.xx"
/>
</div>
<div class="col mb-0">
<label for="dobWithTitle" class="form-label">"DOB"</label>
<input
type="text"
id="dobWithTitle"
class="form-control"
placeholder="DD / MM / YY"
/>
</div>
<div class="col-md">
<NextReservations app_opener=app_opener cancel_opener=cancel_opener/>
</div>
</ModalBody>
<ModalFooter>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal" on:click=move |_| dialog.hide()>
"Close"
</button>
<button type="button" class="btn btn-primary">"Save changes"</button>
</ModalFooter>
</ModalDialog>
<h1>"Welcome to Leptos!"</h1>
<button on:click=on_click>"Click Me: " {count}</button>
<button on:click=move |_| dialog.show()>"Dialog"</button>
<p>{trl("testik!")}</p>
</div>
}
}
+2
View File
@@ -15,4 +15,6 @@ mod properties;
mod property_edit;
mod property_delete;
mod res_dialogs;
mod today_reservations;
mod new_reservations;
+150
View File
@@ -0,0 +1,150 @@
use leptos::*;
use crate::backend::data::{ApiResponse, ResSumWithItems};
use crate::backend::reservation::{Approve, Cancel, get_new_reservations};
use crate::components::data_form::QuestionDialog;
use crate::components::modal_box::{DialogOpener, ModalBody, ModalDialog};
use crate::components::user_menu::MenuOpener;
use crate::locales::{loc_date, show_day, trl};
use crate::pages::public::Public;
use chrono::Datelike;
#[component]
fn approve_dialog(reservation: ReadSignal<ResSumWithItems>, opener: DialogOpener) -> impl IntoView {
let approve = create_server_action::<Approve>();
view! {
<QuestionDialog opener=opener action=approve title="Approve booking">
<input type="hidden" prop:value=move || reservation.get().summary.uuid.to_string() name="uuid"/>
<div>
{trl("Approve booking on ")}
{move || loc_date(reservation.get().summary.date)}
{trl(" for ")}
{move || reservation.get().customer.full_name}"?"
</div>
</QuestionDialog>
}
}
#[component]
fn cancel_dialog(reservation: ReadSignal<ResSumWithItems>, opener: DialogOpener) -> impl IntoView {
let cancel = create_server_action::<Cancel>();
view! {
<QuestionDialog opener=opener action=cancel title="Cancel booking">
<input type="hidden" prop:value=move || reservation.get().summary.uuid.to_string() name="uuid"/>
<div>
{trl("Cancel booking on ")}
{move || loc_date(reservation.get().summary.date)}
{trl(" for ")}
{move || reservation.get().customer.full_name}"?"
</div>
</QuestionDialog>
}
}
#[component]
fn create_dialog(opener: DialogOpener) -> impl IntoView {
view! {
<ModalDialog opener=opener title="Create booking">
<ModalBody>
<Public/>
</ModalBody>
</ModalDialog>
}
}
#[component]
pub fn new_reservations(app_opener: DialogOpener, cancel_opener: DialogOpener) -> impl IntoView {
let create = DialogOpener::new();
let res = create_blocking_resource(move || app_opener.visible() || cancel_opener.visible() || create.visible(),
move |_| get_new_reservations());
let reservation = create_rw_signal(ResSumWithItems::default());
view! {
<ApproveDialog opener=app_opener reservation=reservation.read_only() />
<CancelDialog opener=cancel_opener reservation=reservation.read_only() />
<CreateDialog opener=create />
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title"><i class="bx bx-basket"></i>" "{trl("New bookings")}</h5>
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
{move || {
res.get().map(|r| match r {
Err(e) => {
view! {<div>{trl("Something went wrong")}<br/>{e.to_string()}</div>}}
Ok(r) => { match r {
ApiResponse::Data(r) => {
view! {
<div>
<For each=move || r.clone()
key=|res| res.summary.id()
let:data>
{move || {
let menu = MenuOpener::new();
let data = data.clone();
let app_data = data.clone();
let cancel_data = data.clone();
view! {
<b>{show_day(&data.summary.date.weekday())}" - "{loc_date(data.summary.date)}</b><br/>
<For each=move || data.reservations.clone()
key=|item| item.reservation.id()
let:item>
{item.property.name}": "{item.reservation.from.to_string()}" - "{item.reservation.to.to_string()}<br/>
</For>
{trl("Customer: ")}{data.customer.full_name}", "<a href={format!("mailto:{}", data.customer.email)}>{data.customer.email}</a>", "{data.customer.phone}<br/>
{move || {
let note = data.summary.note.clone();
let show = note.is_some() && !note.clone().unwrap().is_empty();
view! {
<Show when=move || show>
{trl("Note: ")}{note.clone()}<br/>
</Show>
}
}}
{trl("Price: ")}{data.summary.price.to_string()}<br/>
<table width="100%">
<tr>
<td width="100%"></td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow"
on:click=move |_| menu.toggle()>
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class={move || if menu.visible() {"dropdown-menu show"} else {"dropdown-menu"} }
style="position: absolute; insert: 0px 0px auto; margin: 0px; transform: translate3d(-160px, 0px, 0px);"
on:mouseleave=move |_| menu.toggle()>
<a class="dropdown-item" href="javascript:void(0);" on:click=move |_| {
reservation.set(app_data.clone());
app_opener.show();
}>
<i class="bx bx-edit-alt me-1"></i> {trl("Approve")}</a>
<a class="dropdown-item text-danger" href="javascript:void(0);" on:click=move |_| {
reservation.set(cancel_data.clone());
cancel_opener.show();
}>
<i class="bx bx-trash me-1"></i> {trl("Cancel")}</a>
</div>
</div>
</td>
</tr>
</table>
<hr/>
}
}}
</For>
</div>
}
}
ApiResponse::Error(e) => {view! {<div>{trl(&e)}</div>}}}
}
})
}}
</Transition>
<a href="#" class="card-link" on:click=move |_| create.show()>
<i class="bx bx-plus-circle fs-4 lh-0"></i>
</a>
</div>
</div>
}
}
+1 -13
View File
@@ -3,7 +3,7 @@ use leptos::*;
use crate::backend::data::{DayHours, WeekHours};
use crate::backend::opening_hours::get_hours;
use crate::components::modal_box::DialogOpener;
use crate::locales::trl;
use crate::locales::{show_day, trl};
use crate::pages::hours_edit::EditHours;
fn show_time(tm: &str) -> impl Fn() -> String {
@@ -14,18 +14,6 @@ fn show_time(tm: &str) -> impl Fn() -> String {
}
}
fn show_day(day: &Weekday) -> impl Fn() -> String {
match day {
Weekday::Mon => { trl("Monday") }
Weekday::Tue => { trl("Tuesday") }
Weekday::Wed => { trl("Wednesday") }
Weekday::Thu => { trl("Thursday") }
Weekday::Fri => { trl("Friday") }
Weekday::Sat => { trl("Saturday") }
Weekday::Sun => { trl("Sunday") }
}
}
#[component]
pub fn OpeningHours() -> impl IntoView {
let editor = DialogOpener::new();
+63
View File
@@ -0,0 +1,63 @@
use leptos::*;
use crate::backend::data::ApiResponse;
use crate::backend::reservation::get_next_reservations;
use crate::components::modal_box::DialogOpener;
use crate::locales::{loc_date, show_day, trl};
use chrono::Datelike;
#[component]
pub fn next_reservations(app_opener: DialogOpener, cancel_opener: DialogOpener) -> impl IntoView {
let res = create_blocking_resource( move || app_opener.visible() || cancel_opener.visible(), move |_| get_next_reservations());
view! {
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title"><i class="bx bx-basket"></i>" "{trl("Next approved bookings")}</h5>
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
{move || {
res.get().map(|r| match r {
Err(e) => {
view! {<div>{trl("Something went wrong")}<br/>{e.to_string()}</div>}}
Ok(r) => { match r {
ApiResponse::Data(r) => {
view! {
<div>
<For each=move || r.clone()
key=|res| res.summary.id()
let:data>
{move || {
let data = data.clone();
view! {
<b>{show_day(&data.summary.date.weekday())}" - "{loc_date(data.summary.date)}</b><br/>
<For each=move || data.reservations.clone()
key=|item| item.reservation.id()
let:item>
{item.property.name}": "{item.reservation.from.to_string()}" - "{item.reservation.to.to_string()}<br/>
</For>
{trl("Customer: ")}{data.customer.full_name}", "<a href={format!("mailto:{}", data.customer.email)}>{data.customer.email}</a>", "{data.customer.phone}<br/>
{move || {
let note = data.summary.note.clone();
let show = note.is_some() && !note.clone().unwrap().is_empty();
view! {
<Show when=move || show>
{trl("Note: ")}{note.clone()}<br/>
</Show>
}
}}
{trl("Price: ")}{data.summary.price.to_string()}<br/>
<hr/>
}
}}
</For>
</div>
}
}
ApiResponse::Error(e) => {view! {<div>{trl(&e)}</div>}}}
}
})
}}
</Transition>
</div>
</div>
}
}