Added checkbox to booking form - remember customer

main
Josef Rokos 1 year ago
parent aaf4cd54c3
commit 653249287d

@ -9,6 +9,8 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use sqlx::Error; use sqlx::Error;
use std::ops::DerefMut; use std::ops::DerefMut;
use leptos::expect_context; use leptos::expect_context;
use leptos_actix::extract;
use actix_session::Session;
pub async fn find_customer_by_email(email: &str, tx: &mut Transaction<'_, Postgres>) -> Option<Customer> { pub async fn find_customer_by_email(email: &str, tx: &mut Transaction<'_, Postgres>) -> Option<Customer> {
let customer = query_as::<_, Customer>("SELECT * FROM customer WHERE email = $1") let customer = query_as::<_, Customer>("SELECT * FROM customer WHERE email = $1")
@ -50,6 +52,12 @@ cfg_if! { if #[cfg(feature = "ssr")] {
.await?; .await?;
Ok(find_customer_by_email(email, tx).await.ok_or(Error::RowNotFound)?) Ok(find_customer_by_email(email, tx).await.ok_or(Error::RowNotFound)?)
} }
pub async fn remember_customer(customer: Customer) -> Result<(), ServerFnError> {
let session: Session = extract().await?;
session.insert("customer", customer)?;
Ok(())
}
}} }}
#[server] #[server]
@ -67,3 +75,17 @@ pub async fn get_customers() -> Result<ApiResponse<Vec<Customer>>, ServerFnError
Ok(ApiResponse::Data(customers)) Ok(ApiResponse::Data(customers))
} }
#[server]
pub async fn get_remembered() -> Result<Option<Customer>, ServerFnError> {
use actix_session::*;
use leptos_actix::extract;
let session = extract::<Session>().await;
if session.is_err() {
return Ok(None);
}
Ok(session.unwrap().get::<Customer>("customer").unwrap_or(None))
}

@ -376,7 +376,8 @@ pub struct CrReservation {
email: String, email: String,
#[validate(length(min = 1,message = "Enter your phone number"))] #[validate(length(min = 1,message = "Enter your phone number"))]
phone: String, phone: String,
note: String note: String,
remember: bool
} }
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
@ -420,6 +421,9 @@ impl CrReservation {
pub fn note(&self) -> &str { pub fn note(&self) -> &str {
&self.note &self.note
} }
pub fn remember(&self) -> bool {
self.remember
}
} }
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Default)] #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Default)]

@ -261,6 +261,7 @@ pub async fn create_reservation(reservation: CrReservation, pow: String) -> Resu
use crate::backend::customer::find_customer_by_email; use crate::backend::customer::find_customer_by_email;
use crate::backend::customer::sync_customer_data; use crate::backend::customer::sync_customer_data;
use crate::backend::customer::create_customer; use crate::backend::customer::create_customer;
use crate::backend::customer::remember_customer;
use crate::backend::property::get_prop_by_id; use crate::backend::property::get_prop_by_id;
use crate::backend::data::{TmCheck, ReservationState, Reservations}; use crate::backend::data::{TmCheck, ReservationState, Reservations};
use std::collections::HashMap; use std::collections::HashMap;
@ -272,6 +273,15 @@ pub async fn create_reservation(reservation: CrReservation, pow: String) -> Resu
Pow::validate(&pow)?; Pow::validate(&pow)?;
if reservation.remember() {
remember_customer(Customer::new(
0,
reservation.full_name().to_string(),
reservation.email().to_string(),
reservation.phone().to_string(),
0)).await?;
}
let slots = reservation.slots().iter().fold(HashMap::new(), |mut map, s| { let slots = reservation.slots().iter().fold(HashMap::new(), |mut map, s| {
let slot_str = s.split("|").collect::<Vec<_>>(); let slot_str = s.split("|").collect::<Vec<_>>();
map.entry(i32::from_str(slot_str.get(1).unwrap_or(&"")).unwrap_or(0)) map.entry(i32::from_str(slot_str.get(1).unwrap_or(&"")).unwrap_or(0))

@ -131,7 +131,8 @@ lazy_static! {
("Active", "Aktivní"), ("Active", "Aktivní"),
("Are you sure you want to delete property ", "Opravdu chcete smazat předmět "), ("Are you sure you want to delete property ", "Opravdu chcete smazat předmět "),
("Delete property", "Smazat předmět"), ("Delete property", "Smazat předmět"),
("Are you sure you want to delete user ", "Opravdu chcete smazat uživatele ") ("Are you sure you want to delete user ", "Opravdu chcete smazat uživatele "),
("Remember for next time", "Zapamatovat pro příště")
])), ])),
("sk", HashMap::from( [ ("sk", HashMap::from( [
("Dashboard", "Prehlad"), ("Dashboard", "Prehlad"),

@ -3,7 +3,8 @@ use leptos::*;
use leptos_captcha::{Captcha, pow_dispatch}; use leptos_captcha::{Captcha, pow_dispatch};
use leptos_router::*; use leptos_router::*;
use rust_decimal::Decimal; use rust_decimal::Decimal;
use crate::backend::data::{ApiResponse, DayHour, Reservation, ResProperty, SlotType, TmCheck}; use crate::backend::customer::get_remembered;
use crate::backend::data::{ApiResponse, Customer, DayHour, Reservation, ResProperty, SlotType, TmCheck};
use crate::backend::reservation::{CreateReservation, get_public_form_data, is_reserved}; use crate::backend::reservation::{CreateReservation, get_public_form_data, is_reserved};
use crate::backend::user::get_pow; use crate::backend::user::get_pow;
use crate::components::data_form::ForValidation; use crate::components::data_form::ForValidation;
@ -100,6 +101,9 @@ pub fn Public() -> impl IntoView {
let result_dlg = DialogOpener::new(); let result_dlg = DialogOpener::new();
let result = cr_reservation.value(); let result = cr_reservation.value();
let is_pending = create_rw_signal(None); let is_pending = create_rw_signal(None);
let active_str = create_rw_signal("true".to_string());
let get_customer = create_blocking_resource(||(), move |_| get_remembered());
let customer = create_rw_signal(Customer::default());
create_effect(move |_| { create_effect(move |_| {
day.set(Local::now().date_naive()); day.set(Local::now().date_naive());
@ -157,6 +161,14 @@ pub fn Public() -> impl IntoView {
</div> </div>
<Transition fallback=|| view! {<p>{trl("Loading...")}</p> }> <Transition fallback=|| view! {<p>{trl("Loading...")}</p> }>
{move || { {move || {
get_customer.get().map(|c| match c {
Err(_) => {},
Ok(c) => {
if let Some(c) = c {
customer.set(c);
}
}
});
form_data.get().map(|u| match u { form_data.get().map(|u| match u {
Err(e) => { Err(e) => {
view! {<div>{e.to_string()}</div>}} view! {<div>{e.to_string()}</div>}}
@ -216,7 +228,7 @@ pub fn Public() -> impl IntoView {
id="full_name" id="full_name"
class="form-control" class="form-control"
placeholder={trl("Enter full name")} placeholder={trl("Enter full name")}
//prop:value={move || opener.empty()} prop:value={move || customer.get().full_name}
name="reservation[full_name]" name="reservation[full_name]"
/> />
</div> </div>
@ -229,7 +241,7 @@ pub fn Public() -> impl IntoView {
id="email" id="email"
class="form-control" class="form-control"
placeholder={trl("Enter e-mail address")} placeholder={trl("Enter e-mail address")}
//prop:value={move || opener.empty()} prop:value={move || customer.get().email}
name="reservation[email]" name="reservation[email]"
/> />
</div> </div>
@ -242,7 +254,7 @@ pub fn Public() -> impl IntoView {
id="phone" id="phone"
class="form-control" class="form-control"
placeholder={trl("Enter phone number")} placeholder={trl("Enter phone number")}
//prop:value={move || opener.empty()} prop:value={move || customer.get().phone}
name="reservation[phone]" name="reservation[phone]"
/> />
</div> </div>
@ -260,6 +272,15 @@ pub fn Public() -> impl IntoView {
/> />
</div> </div>
</div> </div>
<input
type="checkbox"
id="remember"
class="form-check-input"
checked="true"
on:change=move |ev| active_str.set(if event_target_checked(&ev)
{ "true".to_string() } else { "false".to_string() }) />
<label for="remember" class="form-label">{trl("Remember for next time")}</label>
<input type="hidden" prop:value=active_str name="reservation[remember]"/>
<Captcha is_pending /> <Captcha is_pending />
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">

Loading…
Cancel
Save