#26 - Loading animation + success message fix
This commit was merged in pull request #31.
This commit is contained in:
@@ -64,15 +64,18 @@ class RsvReservationSelector extends HTMLElement {
|
||||
const cal_el = document.createElement('div');
|
||||
cal_el.classList.add('rsv-calendar');
|
||||
|
||||
// Create rsv-timeline with timetable-id set before appending so
|
||||
// connectedCallback sees the correct attribute on first render.
|
||||
// Set up the calendar while cal_el is detached so the initial set_date()
|
||||
// change event fires on a disconnected node and cannot reach any listener.
|
||||
this._calendar = RsvCalendarPicker.create(cal_el, this.inputName);
|
||||
|
||||
// Set timetable-id and the initial date before connecting so connectedCallback
|
||||
// sees both attributes and renders the correct date on first attach.
|
||||
const time_el = document.createElement('rsv-timeline');
|
||||
time_el.setAttribute('timetable-id', this.timetableId);
|
||||
time_el.date = this._calendar.date;
|
||||
|
||||
this.append(cal_el, time_el);
|
||||
|
||||
this._calendar = RsvCalendarPicker.create(cal_el, this.inputName);
|
||||
|
||||
// Date change: clear selection, then push new date to the timeline element.
|
||||
cal_el.addEventListener('change', () => {
|
||||
this.querySelectorAll('.rsv-slots-slot-selected').forEach(s => s.classList.remove('rsv-slots-slot-selected'));
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { render_slot_items } from '../templating/RsvSlotItems.js';
|
||||
|
||||
class RsvReservationSummary extends HTMLElement {
|
||||
|
||||
// ---- Lifecycle ----------------------------------------------------------
|
||||
@@ -25,31 +27,6 @@ class RsvReservationSummary extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Public API ---------------------------------------------------------
|
||||
|
||||
// Detached, static copy of the current selection for the success message.
|
||||
// Mirrors the live layout minus the interactive "clear all" control.
|
||||
snapshot() {
|
||||
const s = ReservairStrings.summary;
|
||||
const list = this.querySelector('.rsv-summary-list');
|
||||
const count = this.querySelector('.rsv-summary-count');
|
||||
const price = this.querySelector('.rsv-summary-price');
|
||||
|
||||
const node = document.createElement('div');
|
||||
node.className = 'rsv-summary rsv-summary-snapshot';
|
||||
node.innerHTML = `
|
||||
<div class="rsv-summary-header">
|
||||
<span class="rsv-summary-title">${s.title}</span>
|
||||
</div>
|
||||
<ul class="rsv-summary-list">${list ? list.innerHTML : ''}</ul>
|
||||
<div class="rsv-summary-footer">
|
||||
<span class="rsv-summary-count">${count ? count.textContent : ''}</span>
|
||||
<div class="rsv-summary-price">${price ? price.textContent : ''}</div>
|
||||
</div>
|
||||
`;
|
||||
return node;
|
||||
}
|
||||
|
||||
// ---- Private ------------------------------------------------------------
|
||||
|
||||
_build() {
|
||||
@@ -74,7 +51,7 @@ class RsvReservationSummary extends HTMLElement {
|
||||
|
||||
_render() {
|
||||
const all_slots = [...this._all_slots.values()].flatMap(({ slots, price_per_block }) =>
|
||||
slots.map(s => ({ ...s, price_per_block }))
|
||||
slots.map(s => ({ ...s, price: price_per_block }))
|
||||
);
|
||||
|
||||
const n = all_slots.length;
|
||||
@@ -92,23 +69,9 @@ class RsvReservationSummary extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const time_opts = { hour: '2-digit', minute: '2-digit' };
|
||||
list.replaceChildren(...all_slots.map(slot => {
|
||||
const start = new Date(slot.start_utc);
|
||||
const end = new Date(slot.end_utc);
|
||||
const li = document.createElement('li');
|
||||
li.className = 'rsv-summary-item';
|
||||
li.innerHTML = `
|
||||
<div class="rsv-summary-item-info">
|
||||
<span class="rsv-summary-item-date">${start.toLocaleDateString(locale, { weekday: 'long', day: 'numeric', month: 'long' })}</span>
|
||||
<span class="rsv-summary-item-time">${start.toLocaleTimeString(locale, time_opts)} – ${end.toLocaleTimeString(locale, time_opts)}</span>
|
||||
</div>
|
||||
${slot.price_per_block > 0 ? `<span class="rsv-summary-item-price">${slot.price_per_block} ${s.currency}</span>` : ''}
|
||||
`;
|
||||
return li;
|
||||
}));
|
||||
list.replaceChildren(...render_slot_items(all_slots, locale, s.currency));
|
||||
|
||||
const total = all_slots.reduce((sum, slot) => sum + slot.price_per_block, 0);
|
||||
const total = all_slots.reduce((sum, slot) => sum + slot.price, 0);
|
||||
count_el.textContent = this._fmt_count(n);
|
||||
price_el.textContent = total > 0 ? `${total} ${s.currency}` : '';
|
||||
}
|
||||
|
||||
@@ -53,6 +53,24 @@ class RsvTimeline extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
_skeleton() {
|
||||
const frag = document.createDocumentFragment();
|
||||
const label = document.createElement('div');
|
||||
label.className = 'rsv-slots-label rsv-slots-skeleton rsv-slots-skeleton-label';
|
||||
frag.appendChild(label);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'rsv-slots-slot rsv-slots-skeleton';
|
||||
const time = document.createElement('span');
|
||||
time.className = 'rsv-slots-slot-time rsv-slots-skeleton rsv-slots-skeleton-text';
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'rsv-slots-slot-badge rsv-slots-skeleton rsv-slots-skeleton-badge';
|
||||
row.append(time, badge);
|
||||
frag.appendChild(row);
|
||||
}
|
||||
return frag;
|
||||
}
|
||||
|
||||
async _render() {
|
||||
// Version guard: discard renders that were superseded by a newer call.
|
||||
const v = ++this._version;
|
||||
@@ -63,6 +81,8 @@ class RsvTimeline extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
|
||||
this.replaceChildren(this._skeleton());
|
||||
|
||||
try {
|
||||
const occupancy = await RsvTimetableService.get_availability_for_date(this.timetableId, this.date);
|
||||
if (v !== this._version) return;
|
||||
|
||||
Reference in New Issue
Block a user