Added layout options for the public booking form. Fixes #16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ use crate::backend::appearance::{DeleteBanner, DeleteLogo, get_appearance, Updat
|
||||
use crate::backend::banner::{image_extension, BannerOptions, ImageKind, IMAGE_ACCEPT, MAX_DIM, MAX_IMAGE_SIZE};
|
||||
use crate::backend::color::{Colors, PALETTES};
|
||||
use crate::backend::data::{banner_style, ApiResponse, Appearance};
|
||||
use crate::backend::layout::LayoutOptions;
|
||||
use crate::components::data_form::{DataForm, QuestionDialog};
|
||||
use crate::components::modal_box::DialogOpener;
|
||||
use crate::locales::trl;
|
||||
@@ -15,6 +16,7 @@ use crate::locales::trl;
|
||||
struct Look {
|
||||
colors: RwSignal<Colors>,
|
||||
banner: RwSignal<BannerOptions>,
|
||||
layout: RwSignal<LayoutOptions>,
|
||||
/// Images selected in the file inputs and not uploaded yet, as object URLs for the preview
|
||||
new_banner: RwSignal<Option<String>>,
|
||||
new_logo: RwSignal<Option<String>>,
|
||||
@@ -276,7 +278,8 @@ fn preview(look: Look, appearance: ReadSignal<Appearance>) -> impl IntoView {
|
||||
};
|
||||
|
||||
view! {
|
||||
<div class="public-page public-preview" style=move || look.colors.get().style()>
|
||||
<div class=move || format!("public-page public-preview {}", look.layout.get().classes())
|
||||
style=move || look.colors.get().style()>
|
||||
<Show when=has_banner>
|
||||
<div class="public-banner" style=move || banner_style(image(), &look.banner.get()) on:click=set_focus>
|
||||
{move || logo().map(|url| view! {
|
||||
@@ -292,8 +295,8 @@ fn preview(look: Look, appearance: ReadSignal<Appearance>) -> impl IntoView {
|
||||
</div>
|
||||
</Show>
|
||||
<div class="public-banner-text" inner_html=move || appearance.get().text.unwrap_or_default()></div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="card-body public-form">
|
||||
<div class="row public-form-columns">
|
||||
<div class="col-sm">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
@@ -423,12 +426,29 @@ fn banner_settings(
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves colours and banner options together, they make one look
|
||||
#[component]
|
||||
fn save_bar(look: Look, saved: RwSignal<(Colors, BannerOptions)>) -> impl IntoView {
|
||||
fn layout_settings(layout: RwSignal<LayoutOptions>) -> impl IntoView {
|
||||
view! {
|
||||
<Choice label="Content width" options=vec![("full", "Full width"), ("centered", "Centred")]
|
||||
value=move || layout.get().width on_change=move |v| layout.update(|l| l.width = v)/>
|
||||
<Choice label="Form" options=vec![("columns", "Side by side"), ("stacked", "Stacked")]
|
||||
value=move || layout.get().arrangement on_change=move |v| layout.update(|l| l.arrangement = v)/>
|
||||
<Choice label="Time slots" options=vec![("normal", "Normal"), ("large", "Large")]
|
||||
value=move || layout.get().slots on_change=move |v| layout.update(|l| l.slots = v)/>
|
||||
<Choice label="Corners" options=vec![("rounded", "Rounded"), ("square", "Square")]
|
||||
value=move || layout.get().corners on_change=move |v| layout.update(|l| l.corners = v)/>
|
||||
}
|
||||
}
|
||||
|
||||
/// Everything that is saved together as one look
|
||||
type SavedLook = (Colors, BannerOptions, LayoutOptions);
|
||||
|
||||
/// Saves colours, banner options and layout together, they make one look
|
||||
#[component]
|
||||
fn save_bar(look: Look, saved: RwSignal<SavedLook>) -> impl IntoView {
|
||||
let save = ServerAction::<UpdateStyle>::new();
|
||||
let sent = StoredValue::new((Colors::default(), BannerOptions::default()));
|
||||
let current = move || (look.colors.get(), look.banner.get());
|
||||
let sent = StoredValue::new(SavedLook::default());
|
||||
let current = move || (look.colors.get(), look.banner.get(), look.layout.get());
|
||||
let dirty = move || current() != saved.get();
|
||||
|
||||
Effect::new(move |_| {
|
||||
@@ -440,15 +460,16 @@ fn save_bar(look: Look, saved: RwSignal<(Colors, BannerOptions)>) -> impl IntoVi
|
||||
view! {
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary me-2" on:click=move |_| {
|
||||
let (colors, banner) = current();
|
||||
sent.set_value((colors.clone(), banner.clone()));
|
||||
save.dispatch(UpdateStyle { colors, banner });
|
||||
let (colors, banner, layout) = current();
|
||||
sent.set_value((colors.clone(), banner.clone(), layout.clone()));
|
||||
save.dispatch(UpdateStyle { colors, banner, layout });
|
||||
}>
|
||||
{trl("Save")}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" on:click=move |_| {
|
||||
look.colors.set(Colors::default());
|
||||
look.banner.set(BannerOptions::default());
|
||||
look.layout.set(LayoutOptions::default());
|
||||
}>
|
||||
{trl("Reset to default")}
|
||||
</button>
|
||||
@@ -493,10 +514,11 @@ pub fn appearance() -> impl IntoView {
|
||||
let look = Look {
|
||||
colors: RwSignal::new(Colors::default()),
|
||||
banner: RwSignal::new(BannerOptions::default()),
|
||||
layout: RwSignal::new(LayoutOptions::default()),
|
||||
new_banner: RwSignal::new(None),
|
||||
new_logo: RwSignal::new(None),
|
||||
};
|
||||
let saved = RwSignal::new((Colors::default(), BannerOptions::default()));
|
||||
let saved = RwSignal::new(SavedLook::default());
|
||||
let app_preview = RwSignal::new(Appearance::default());
|
||||
let look_loaded = StoredValue::new(false);
|
||||
|
||||
@@ -507,7 +529,8 @@ pub fn appearance() -> impl IntoView {
|
||||
if !look_loaded.get_value() {
|
||||
look.colors.set(app.colors.clone());
|
||||
look.banner.set(app.banner_options.clone());
|
||||
saved.set((app.colors.clone(), app.banner_options.clone()));
|
||||
look.layout.set(app.layout.clone());
|
||||
saved.set((app.colors.clone(), app.banner_options.clone(), app.layout.clone()));
|
||||
look_loaded.set_value(true);
|
||||
}
|
||||
app_preview.set(app);
|
||||
@@ -576,6 +599,12 @@ pub fn appearance() -> impl IntoView {
|
||||
delete_banner=del_banner delete_logo=del_logo/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{trl("Layout")}</h5>
|
||||
<LayoutSettings layout=look.layout/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<SaveBar look=look saved=saved/>
|
||||
|
||||
+5
-5
@@ -120,10 +120,10 @@ pub fn PublicPage() -> impl IntoView {
|
||||
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
|
||||
{move || {
|
||||
appearance.get().map(|a| {
|
||||
// Default colours when the settings can't be loaded, the form reports the error itself
|
||||
let style = a.map(|a| a.colors).unwrap_or_default().style();
|
||||
// Default look when the settings can't be loaded, the form reports the error itself
|
||||
let a = a.unwrap_or_default();
|
||||
view! {
|
||||
<div class="public-page" style=style>
|
||||
<div class=format!("public-page {}", a.layout.classes()) style=a.colors.style()>
|
||||
<Public/>
|
||||
</div>
|
||||
}
|
||||
@@ -193,7 +193,7 @@ pub fn Public() -> impl IntoView {
|
||||
}}
|
||||
</Transition>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="card-body public-form">
|
||||
<ActionForm
|
||||
// Capture phase, so the form is validated and the captcha solved before it is dispatched
|
||||
on:submit:capture=move |ev: SubmitEvent| {
|
||||
@@ -213,7 +213,7 @@ pub fn Public() -> impl IntoView {
|
||||
}
|
||||
}
|
||||
action=cr_reservation>
|
||||
<div class="row mb-5">
|
||||
<div class="row mb-5 public-form-columns">
|
||||
<div class="col-md">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
|
||||
Reference in New Issue
Block a user