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