Implemented admin overview.
parent
6c7fd2e46f
commit
e69db7f40b
@ -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>
|
||||
}
|
||||
}
|
@ -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>
|
||||
}
|
||||
}
|
@ -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>
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue