Refine appointment booking calendar layout
This commit is contained in:
parent
27320bc0ac
commit
630a0f5046
@ -16,6 +16,7 @@
|
||||
],
|
||||
"assets": {
|
||||
"web.assets_frontend": [
|
||||
"mcs_appointment_booking/static/src/js/appointment_booking.js",
|
||||
"mcs_appointment_booking/static/src/scss/appointment_booking.scss",
|
||||
],
|
||||
},
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function initAppointmentBooking(root) {
|
||||
const startInput = root.querySelector(".js-mcs-start");
|
||||
const stopInput = root.querySelector(".js-mcs-stop");
|
||||
const selectedLabel = root.querySelector(".js-mcs-selected-slot");
|
||||
const buttons = root.querySelectorAll(".mcs-appt-time");
|
||||
|
||||
if (!startInput || !stopInput || !selectedLabel || !buttons.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
buttons.forEach((item) => item.classList.remove("active"));
|
||||
button.classList.add("active");
|
||||
startInput.value = button.dataset.start || "";
|
||||
stopInput.value = button.dataset.stop || "";
|
||||
selectedLabel.textContent = button.dataset.label || button.textContent.trim();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.querySelectorAll(".mcs-appointment").forEach(initAppointmentBooking);
|
||||
});
|
||||
})();
|
||||
@ -22,9 +22,9 @@
|
||||
|
||||
.mcs-appt-shell {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 380px) minmax(0, 1fr);
|
||||
grid-template-columns: minmax(260px, 340px) minmax(0, 1fr);
|
||||
gap: 0;
|
||||
max-width: 1180px;
|
||||
max-width: 1240px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
@ -34,13 +34,13 @@
|
||||
}
|
||||
|
||||
.mcs-appt-info {
|
||||
padding: 34px;
|
||||
padding: 30px;
|
||||
background: var(--mcs-appt-navy);
|
||||
color: #ffffff;
|
||||
|
||||
h1 {
|
||||
margin: 18px 0 18px;
|
||||
font-size: clamp(2rem, 5vw, 3.45rem);
|
||||
font-size: clamp(2rem, 4vw, 3rem);
|
||||
line-height: 1.04;
|
||||
font-weight: 820;
|
||||
}
|
||||
@ -89,7 +89,7 @@
|
||||
|
||||
.mcs-appt-booking {
|
||||
min-width: 0;
|
||||
padding: 32px;
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.mcs-appt-section-head {
|
||||
@ -121,15 +121,16 @@
|
||||
|
||||
.mcs-appt-date-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(82px, 1fr));
|
||||
gap: 10px;
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.mcs-appt-date {
|
||||
display: grid;
|
||||
min-height: 82px;
|
||||
min-height: 66px;
|
||||
place-items: center;
|
||||
padding: 10px;
|
||||
padding: 8px 6px;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
@ -146,7 +147,7 @@
|
||||
|
||||
span {
|
||||
color: var(--mcs-appt-ink);
|
||||
font-size: 1.35rem;
|
||||
font-size: 1.18rem;
|
||||
line-height: 1;
|
||||
font-weight: 850;
|
||||
}
|
||||
@ -181,65 +182,69 @@
|
||||
background: var(--mcs-appt-soft);
|
||||
}
|
||||
|
||||
.mcs-appt-slot-list {
|
||||
.mcs-appt-workspace {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(300px, 360px);
|
||||
gap: 20px;
|
||||
margin-top: 28px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.mcs-appt-slot {
|
||||
input[type="radio"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mcs-appt-times,
|
||||
.mcs-appt-details {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-height: 56px;
|
||||
padding: 12px 14px;
|
||||
.mcs-appt-selected-day {
|
||||
margin: -8px 0 14px;
|
||||
color: var(--mcs-appt-muted);
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.mcs-appt-slot-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
|
||||
gap: 9px;
|
||||
max-height: 540px;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.mcs-appt-time {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
min-height: 66px;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
background: #ffffff;
|
||||
color: var(--mcs-appt-ink);
|
||||
cursor: pointer;
|
||||
transition: border-color 160ms ease-out, background 160ms ease-out, transform 160ms ease-out;
|
||||
text-align: left;
|
||||
transition: border-color 160ms ease-out, background 160ms ease-out, box-shadow 160ms ease-out, transform 160ms ease-out;
|
||||
|
||||
span {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--mcs-appt-teal);
|
||||
color: var(--mcs-appt-muted);
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
label:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
&.active {
|
||||
border-color: var(--mcs-appt-blue);
|
||||
background: #eef4ff;
|
||||
box-shadow: inset 0 0 0 1px rgba(28, 78, 216, 0.15);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked ~ .mcs-appt-form {
|
||||
display: grid;
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-form {
|
||||
display: none;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
margin-top: 10px;
|
||||
padding: 18px;
|
||||
border: 1px solid #cbd8f7;
|
||||
border-radius: 12px;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.mcs-appt-form-grid {
|
||||
@ -292,6 +297,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-details {
|
||||
position: sticky;
|
||||
top: 96px;
|
||||
padding: 18px;
|
||||
border: 1px solid #cbd8f7;
|
||||
border-radius: 14px;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.mcs-appt-selected-slot {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
|
||||
small {
|
||||
color: var(--mcs-appt-muted);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.mcs-appt-shell {
|
||||
grid-template-columns: 1fr;
|
||||
@ -301,6 +337,14 @@
|
||||
.mcs-appt-booking {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.mcs-appt-workspace {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-appt-details {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
@ -308,11 +352,14 @@
|
||||
padding: 18px 12px 42px;
|
||||
}
|
||||
|
||||
.mcs-appt-date-strip,
|
||||
.mcs-appt-form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-appt-date-strip {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.mcs-appt-section-head {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
@ -66,30 +66,45 @@
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<header class="mcs-appt-section-head mcs-appt-section-head--spaced">
|
||||
<section class="mcs-appt-workspace">
|
||||
<div class="mcs-appt-times">
|
||||
<header class="mcs-appt-section-head">
|
||||
<span>Step 2</span>
|
||||
<h2>Choose a time on <t t-esc="selected_day.strftime('%A, %d %B')"/></h2>
|
||||
<h2>Available times</h2>
|
||||
</header>
|
||||
<p class="mcs-appt-selected-day" t-esc="selected_day.strftime('%A, %d %B')"/>
|
||||
|
||||
<div t-if="not slots" class="mcs-appt-empty">
|
||||
No free slots are available on this day.
|
||||
</div>
|
||||
|
||||
<div t-if="slots" class="mcs-appt-slot-list">
|
||||
<div t-if="slots" class="mcs-appt-slot-grid">
|
||||
<t t-foreach="slots" t-as="slot">
|
||||
<div class="mcs-appt-slot">
|
||||
<input type="radio"
|
||||
name="selected_slot"
|
||||
t-att-id="'mcs_slot_%s' % slot_index"
|
||||
t-att-checked="'checked' if slot_index == 0 else None"/>
|
||||
<label t-att-for="'mcs_slot_%s' % slot_index">
|
||||
<span t-esc="slot['label']"/>
|
||||
<small>Available</small>
|
||||
</label>
|
||||
<form t-att-action="'/appointments/%s/book' % appointment_type.id" method="post" class="mcs-appt-form">
|
||||
<button type="button"
|
||||
t-attf-class="mcs-appt-time #{'active' if slot_index == 0 else ''}"
|
||||
t-att-data-start="slot['start']"
|
||||
t-att-data-stop="slot['stop']"
|
||||
t-att-data-label="slot['label']">
|
||||
<span t-esc="slot['label'].split(' - ')[0]"/>
|
||||
<small t-esc="slot['label'].split(' - ')[1]"/>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="mcs-appt-details">
|
||||
<header class="mcs-appt-section-head">
|
||||
<span>Step 3</span>
|
||||
<h2>Your details</h2>
|
||||
</header>
|
||||
<div t-if="slots" class="mcs-appt-selected-slot">
|
||||
<small>Selected time</small>
|
||||
<strong class="js-mcs-selected-slot" t-esc="slots[0]['label']"/>
|
||||
</div>
|
||||
<form t-if="slots" t-att-action="'/appointments/%s/book' % appointment_type.id" method="post" class="mcs-appt-form">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="start" t-att-value="slot['start']"/>
|
||||
<input type="hidden" name="stop" t-att-value="slot['stop']"/>
|
||||
<input type="hidden" name="start" class="js-mcs-start" t-att-value="slots[0]['start']"/>
|
||||
<input type="hidden" name="stop" class="js-mcs-stop" t-att-value="slots[0]['stop']"/>
|
||||
<div class="mcs-appt-form-grid">
|
||||
<label>Name<input name="customer_name" required="required"/></label>
|
||||
<label>Email<input name="customer_email" type="email" required="required"/></label>
|
||||
@ -98,9 +113,12 @@
|
||||
</div>
|
||||
<button type="submit">Confirm Appointment</button>
|
||||
</form>
|
||||
<div t-if="not slots" class="mcs-appt-selected-slot">
|
||||
<small>Selected time</small>
|
||||
<strong>Choose another date</strong>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user