Improved card with new reservations.
This commit is contained in:
Generated
+1
-1
@@ -3211,7 +3211,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rezervator"
|
name = "rezervator"
|
||||||
version = "1.1.2"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rezervator"
|
name = "rezervator"
|
||||||
version = "1.1.2"
|
version = "1.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
a.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
@@ -17,6 +17,95 @@ pub enum ApiResponse<T> {
|
|||||||
Error(String)
|
Error(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Page {
|
||||||
|
page_size: i32,
|
||||||
|
page_num: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
pub fn new(page_size: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
page_size,
|
||||||
|
page_num: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_page(self, page_num: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
page_num,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(&self) -> i32 {
|
||||||
|
if self.page_size == 0 {
|
||||||
|
i32::MAX
|
||||||
|
} else {
|
||||||
|
self.page_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> i32 {
|
||||||
|
(self.page_num - 1) * self.page_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn page_num(&self) -> i32 {
|
||||||
|
self.page_num
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first(&mut self) {
|
||||||
|
self.page_num = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev(&mut self) {
|
||||||
|
if self.page_num > 1 {
|
||||||
|
self.page_num = self.page_num - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) {
|
||||||
|
self.page_num = self.page_num + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to(&mut self, page: i32) {
|
||||||
|
self.page_num = page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct PagedResponse<T> {
|
||||||
|
page: Page,
|
||||||
|
record_count: i64,
|
||||||
|
data: Vec<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> PagedResponse<T> {
|
||||||
|
pub fn new(data: Vec<T>, page: Page, record_count: i64) -> Self {
|
||||||
|
Self {
|
||||||
|
page,
|
||||||
|
record_count,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_first(&self) -> bool {
|
||||||
|
self.page.page_num() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_last(&self) -> bool {
|
||||||
|
self.page.offset() as i64 + self.page.limit() as i64 >= self.record_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_pages(&self) -> i32 {
|
||||||
|
(self.record_count as f64 / self.page.limit() as f64).ceil() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> Vec<T> {
|
||||||
|
self.data.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
|
||||||
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
|
||||||
pub struct Company {
|
pub struct Company {
|
||||||
|
|||||||
+23
-14
@@ -1,6 +1,6 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
use crate::backend::data::{ApiResponse, CrReservation, Reservation, PublicFormData, ResSumWithItems};
|
use crate::backend::data::{ApiResponse, CrReservation, Reservation, PublicFormData, ResSumWithItems, Page, PagedResponse};
|
||||||
use crate::components::data_form::ForValidation;
|
use crate::components::data_form::ForValidation;
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use chrono::{NaiveDate, NaiveTime};
|
use chrono::{NaiveDate, NaiveTime};
|
||||||
@@ -89,9 +89,9 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn reservations_in_range(from: &NaiveDate, to: &NaiveDate, state: Option<ReservationState>) -> Result<Vec<ResSumWithItems>, ServerFnError> {
|
async fn reservations_in_range(from: &NaiveDate, to: &NaiveDate, state: Option<ReservationState>, page: Page) -> Result<(Vec<ResSumWithItems>, i64), ServerFnError> {
|
||||||
let pool = get_pool().await?;
|
let pool = get_pool().await?;
|
||||||
let view = if let Some(s) = state {
|
let view = if let Some(s) = &state {
|
||||||
query_as::<_, ResAllView>(
|
query_as::<_, ResAllView>(
|
||||||
"SELECT r.from, r.to, p.name, p.description, s.*, c.full_name, c.email, c.phone \
|
"SELECT r.from, r.to, p.name, p.description, s.*, c.full_name, c.email, c.phone \
|
||||||
FROM reservation r \
|
FROM reservation r \
|
||||||
@@ -131,7 +131,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if view.is_empty() {
|
if view.is_empty() {
|
||||||
return Ok(vec![])
|
return Ok((vec![], 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ret = view.into_iter().fold(HashMap::new(), |mut m, v| {
|
let mut ret = view.into_iter().fold(HashMap::new(), |mut m, v| {
|
||||||
@@ -148,7 +148,14 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
ret.sort_by(|a, b| a.summary.date.cmp(&b.summary.date) );
|
ret.sort_by(|a, b| a.summary.date.cmp(&b.summary.date) );
|
||||||
Ok(ret)
|
let count = ret.len() as i64;
|
||||||
|
let last = if page.limit() == i32::MAX || ((page.offset() + page.limit()) as usize > ret.len()) { ret.len() }
|
||||||
|
else { (page.offset() + page.limit()) as usize };
|
||||||
|
let ret_paged = ret[page.offset() as usize..last]
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok((ret_paged, count))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_state(uuid: Uuid, state: ReservationState) -> Result<(), ServerFnError> {
|
async fn set_state(uuid: Uuid, state: ReservationState) -> Result<(), ServerFnError> {
|
||||||
@@ -363,16 +370,18 @@ impl ForValidation for CreateReservation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_new_reservations() -> Result<ApiResponse<Vec<ResSumWithItems>>, ServerFnError> {
|
pub async fn get_new_reservations(page: Page) -> Result<ApiResponse<PagedResponse<ResSumWithItems>>, ServerFnError> {
|
||||||
use crate::perm_check;
|
use crate::perm_check;
|
||||||
use chrono::{Days, Local};
|
use chrono::{Days, Local};
|
||||||
use crate::backend::data::ReservationState;
|
use crate::backend::data::ReservationState;
|
||||||
|
|
||||||
perm_check!(is_logged_in);
|
perm_check!(is_logged_in);
|
||||||
|
|
||||||
Ok(ApiResponse::Data(reservations_in_range(&Local::now().date_naive(),
|
let res = reservations_in_range(&Local::now().date_naive(),
|
||||||
&Local::now().checked_add_days(Days::new(7)).unwrap().date_naive(),
|
&Local::now().checked_add_days(Days::new(60)).unwrap().date_naive(),
|
||||||
Some(ReservationState::New)).await?))
|
Some(ReservationState::New), page).await?;
|
||||||
|
|
||||||
|
Ok(ApiResponse::Data(PagedResponse::new(res.0, page, res.1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
@@ -385,7 +394,7 @@ pub async fn get_next_reservations() -> Result<ApiResponse<Vec<ResSumWithItems>>
|
|||||||
|
|
||||||
Ok(ApiResponse::Data(reservations_in_range(&Local::now().date_naive(),
|
Ok(ApiResponse::Data(reservations_in_range(&Local::now().date_naive(),
|
||||||
&Local::now().checked_add_days(Days::new(7)).unwrap().date_naive(),
|
&Local::now().checked_add_days(Days::new(7)).unwrap().date_naive(),
|
||||||
Some(ReservationState::Approved)).await?))
|
Some(ReservationState::Approved), Page::default()).await?.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
@@ -402,9 +411,9 @@ fn num_days(month: u32, year: i32) -> i64 {
|
|||||||
pub async fn get_reservations_for_month(month: u32, year: i32) -> Result<ApiResponse<Vec<ResSumWithItems>>, ServerFnError> {
|
pub async fn get_reservations_for_month(month: u32, year: i32) -> Result<ApiResponse<Vec<ResSumWithItems>>, ServerFnError> {
|
||||||
let data = reservations_in_range(&NaiveDate::from_ymd_opt(year, month, 1).unwrap(),
|
let data = reservations_in_range(&NaiveDate::from_ymd_opt(year, month, 1).unwrap(),
|
||||||
&NaiveDate::from_ymd_opt(year, month, num_days(month, year).to_u32().unwrap_or_default()).unwrap(),
|
&NaiveDate::from_ymd_opt(year, month, num_days(month, year).to_u32().unwrap_or_default()).unwrap(),
|
||||||
None).await?;
|
None, Page::default()).await?;
|
||||||
|
|
||||||
Ok(ApiResponse::Data(data))
|
Ok(ApiResponse::Data(data.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
@@ -522,8 +531,8 @@ pub async fn reservations_in_month(year: i32, month: u32) -> Result<ApiResponse<
|
|||||||
let ret = reservations_in_range(&NaiveDate::from_ymd_opt(year, month, 1).ok_or(ServerFnError::new("Cannot parse date"))?,
|
let ret = reservations_in_range(&NaiveDate::from_ymd_opt(year, month, 1).ok_or(ServerFnError::new("Cannot parse date"))?,
|
||||||
&NaiveDate::from_ymd_opt(year, month, num_days(month, year).to_u32().unwrap_or_default())
|
&NaiveDate::from_ymd_opt(year, month, num_days(month, year).to_u32().unwrap_or_default())
|
||||||
.ok_or(ServerFnError::new("Cannot parse date"))?,
|
.ok_or(ServerFnError::new("Cannot parse date"))?,
|
||||||
None)
|
None, Page::default())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(ApiResponse::Data(ret))
|
Ok(ApiResponse::Data(ret.0))
|
||||||
}
|
}
|
||||||
+1
-1
@@ -322,7 +322,7 @@ pub async fn delete_user(id: i32) -> Result<ApiResponse<()>, ServerFnError> {
|
|||||||
pub async fn get_pow() -> Result<String, ServerFnError> {
|
pub async fn get_pow() -> Result<String, ServerFnError> {
|
||||||
use leptos_captcha::spow::pow::Pow;
|
use leptos_captcha::spow::pow::Pow;
|
||||||
|
|
||||||
if !cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
Ok(Pow::with_difficulty(10, 10)?.to_string())
|
Ok(Pow::with_difficulty(10, 10)?.to_string())
|
||||||
} else {
|
} else {
|
||||||
Ok(Pow::new(10)?.to_string())
|
Ok(Pow::new(10)?.to_string())
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use crate::components::user_menu::MenuOpener;
|
|||||||
pub fn Header() -> impl IntoView {
|
pub fn Header() -> impl IntoView {
|
||||||
let drawer = use_context::<MenuOpener>().expect("No drawer opener");
|
let drawer = use_context::<MenuOpener>().expect("No drawer opener");
|
||||||
let dlg_helper = use_context::<DialogHelper>().expect("No dialog helper");
|
let dlg_helper = use_context::<DialogHelper>().expect("No dialog helper");
|
||||||
|
//let banner_css = create_signal(String::new());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Html
|
<Html
|
||||||
@@ -39,6 +40,7 @@ pub fn Header() -> impl IntoView {
|
|||||||
<Link rel="stylesheet" href="/vendor/css/theme-default.css" />
|
<Link rel="stylesheet" href="/vendor/css/theme-default.css" />
|
||||||
<Link rel="stylesheet" href="/css/demo.css" />
|
<Link rel="stylesheet" href="/css/demo.css" />
|
||||||
<Link rel="stylesheet" href="/banner.css" />
|
<Link rel="stylesheet" href="/banner.css" />
|
||||||
|
<Link rel="stylesheet" href="/vendor/css/control.css" />
|
||||||
|
|
||||||
//<!-- Vendors CSS -->
|
//<!-- Vendors CSS -->
|
||||||
<Link rel="stylesheet" href="/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />
|
<Link rel="stylesheet" href="/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::backend::data::{ApiResponse, ResSumWithItems};
|
use crate::backend::data::{ApiResponse, Page, ResSumWithItems};
|
||||||
use crate::backend::reservation::{Approve, Cancel, get_new_reservations};
|
use crate::backend::reservation::{Approve, Cancel, get_new_reservations};
|
||||||
use crate::components::data_form::QuestionDialog;
|
use crate::components::data_form::QuestionDialog;
|
||||||
use crate::components::modal_box::{DialogOpener, ModalBody, ModalDialog};
|
use crate::components::modal_box::{DialogOpener, ModalBody, ModalDialog};
|
||||||
@@ -56,8 +56,12 @@ fn create_dialog(opener: DialogOpener) -> impl IntoView {
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn new_reservations(app_opener: DialogOpener, cancel_opener: DialogOpener) -> impl IntoView {
|
pub fn new_reservations(app_opener: DialogOpener, cancel_opener: DialogOpener) -> impl IntoView {
|
||||||
let create = DialogOpener::new();
|
let create = DialogOpener::new();
|
||||||
let res = create_blocking_resource(move || app_opener.visible() || cancel_opener.visible() || create.visible(),
|
let page = create_rw_signal(Page::new(5));
|
||||||
move |_| get_new_reservations());
|
let first = create_rw_signal(false);
|
||||||
|
let last = create_rw_signal(false);
|
||||||
|
let total_pages = create_rw_signal(0i32);
|
||||||
|
let res = create_blocking_resource(move || (app_opener.visible() || cancel_opener.visible() || create.visible(), page),
|
||||||
|
move |d| get_new_reservations(d.1.get()));
|
||||||
let reservation = create_rw_signal(ResSumWithItems::default());
|
let reservation = create_rw_signal(ResSumWithItems::default());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
@@ -74,9 +78,12 @@ pub fn new_reservations(app_opener: DialogOpener, cancel_opener: DialogOpener) -
|
|||||||
view! {<div>{trl("Something went wrong")}<br/>{e.to_string()}</div>}}
|
view! {<div>{trl("Something went wrong")}<br/>{e.to_string()}</div>}}
|
||||||
Ok(r) => { match r {
|
Ok(r) => { match r {
|
||||||
ApiResponse::Data(r) => {
|
ApiResponse::Data(r) => {
|
||||||
|
first.set(r.is_first());
|
||||||
|
last.set(r.is_last());
|
||||||
|
total_pages.set(r.total_pages());
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
<For each=move || r.clone()
|
<For each=move || r.data()
|
||||||
key=|res| res.summary.id()
|
key=|res| res.summary.id()
|
||||||
let:data>
|
let:data>
|
||||||
{move || {
|
{move || {
|
||||||
@@ -144,6 +151,19 @@ pub fn new_reservations(app_opener: DialogOpener, cancel_opener: DialogOpener) -
|
|||||||
<a href="#" class="card-link" on:click=move |_| create.show()>
|
<a href="#" class="card-link" on:click=move |_| create.show()>
|
||||||
<i class="bx bx-plus-circle fs-4 lh-0"></i>
|
<i class="bx bx-plus-circle fs-4 lh-0"></i>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="#" class={move || {if first.get() {"card-link disabled"} else {"card-link"}}} on:click=move |_| page.update(move |p| p.first())>
|
||||||
|
<i class="bx bx-first-page fs-4 lh-0"></i>
|
||||||
|
</a>
|
||||||
|
<a href="#" class={move || {if first.get() {"card-link disabled"} else {"card-link"}}} on:click=move |_| page.update(move |p| p.prev())>
|
||||||
|
<i class="bx bx-chevron-left fs-4 lh-0"></i>
|
||||||
|
</a>
|
||||||
|
<span class="card-link">{move || page.get().page_num()}</span>
|
||||||
|
<a href="#" class={move || {if last.get() {"card-link disabled"} else {"card-link"}}} on:click=move |_| page.update(move |p| p.next())>
|
||||||
|
<i class="bx bx-chevron-right fs-4 lh-0"></i>
|
||||||
|
</a>
|
||||||
|
<a href="#" class={move || {if last.get() {"card-link disabled"} else {"card-link"}}} on:click=move |_| page.update(move |p| p.go_to(total_pages.get()))>
|
||||||
|
<i class="bx bx-last-page fs-4 lh-0"></i>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user