assets/booking.js Repository wynajem_motorowek Original path assets/booking.jsRole SOURCE Size 27384 bytes Lines 753 SHA-256 9c00ffb64ca7ba3e32b047f1225612a4742f8e376c8cc028d4ce8c49344b7df4Displayed range 501–753 Previous file/page · Project index · Next file/page
const payload = {
name: (qs("#dName", state.mountedEl)?.value || "").trim(),
email: customerEmail,
phone: String(qs("#dPhone", state.mountedEl)?.value || "").trim(),
peopleCount: Number(qs("#dPeople", state.mountedEl)?.value || 1),
wantInvoice: state.wantInvoice,
invoice: state.wantInvoice
? {
companyName: qs("#iCompany", state.mountedEl)?.value || "",
nip: invoiceNip,
address: qs("#iAddress", state.mountedEl)?.value || "",
email: qs("#iEmail", state.mountedEl)?.value || "",
}
: null,
};
await api(`/api/holds/${encodeURIComponent(state.holdId)}/details`, "POST", payload);
state.peopleCount = payload.peopleCount;
state.customerEmail = payload.email;
state.step = "payment";
setMessage("Dane zapisane. Dokończ płatność testową.", "ok");
} catch (err) {
const reason = err.payload?.reason || err.message;
if (reason === "EXPIRED") {
state.step = "select";
state.holdId = null;
state.expiresAt = null;
clearCountdown();
setMessage("Za długo – rezerwacja wygasła", "error");
return;
}
if (reason === "CAPACITY_EXCEEDED") {
const max = Number(err.payload?.maxCapacity || resourceCapacity());
setMessage(`Za dużo osób. Maksymalnie ${max} osób dla tej łódki.`, "error");
return;
}
setMessage(`Błąd: ${reason}`, "error");
}
}
async function handlePayment(decision) {
try {
const path =
decision === "confirm"
? "/api/payments/test/confirm"
: decision === "cash"
? "/api/payments/cash/confirm"
: "/api/payments/test/fail";
const result = await api(path, "POST", { holdId: state.holdId });
clearCountdown();
state.holdId = null;
state.expiresAt = null;
if (result?.ok && decision !== "fail") {
const email = encodeURIComponent(state.customerEmail || "");
window.location.href = `/rezerwacja_dziekujemy.html?email=${email}`;
return;
}
state.step = "select";
await loadAvailability();
const successMsg = result?.orderNumber
? `Rezerwacja zakończona. Numer zamówienia: ${result.orderNumber}`
: "Rezerwacja zakończona.";
setMessage(result.ok ? successMsg : "Nie udało się zakończyć płatności.", result.ok ? "ok" : "error");
} catch (err) {
const reason = err.payload?.reason || err.message;
if (reason === "EXPIRED") {
clearCountdown();
state.holdId = null;
state.expiresAt = null;
state.step = "select";
setMessage("Za długo – rezerwacja wygasła", "error");
return;
}
setMessage(`Błąd: ${reason}`, "error");
}
}
async function handleCancelProcess() {
const currentHoldId = state.holdId;
clearCountdown();
state.holdId = null;
state.expiresAt = null;
state.step = "select";
try {
if (currentHoldId) {
await api("/api/payments/test/fail", "POST", { holdId: currentHoldId });
}
} catch (_err) {}
await loadAvailability();
setMessage("Proces anulowany.", "");
}
function bindEvents() {
const root = state.mountedEl;
if (!root) return;
root.querySelectorAll("[data-resource]").forEach((btn) => {
btn.onclick = async () => {
state.selectedResource = btn.getAttribute("data-resource") || "";
const cap = resourceCapacity();
if (state.peopleCount > cap) state.peopleCount = cap;
await loadAvailability();
snapSelectionToFree();
rerender();
};
});
root.querySelectorAll("[data-action='prev-day']").forEach((btn) => {
btn.onclick = async () => {
const d = new Date(`${state.date}T00:00:00`);
d.setDate(d.getDate() - 1);
const nextDate = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
if (isPastDateKey(nextDate)) {
setMessage("Nie można rezerwować w przeszłości.", "error");
return;
}
state.date = nextDate;
await loadAvailability();
snapSelectionToFree();
rerender();
};
});
root.querySelectorAll("[data-action='next-day']").forEach((btn) => {
btn.onclick = async () => {
const d = new Date(`${state.date}T00:00:00`);
d.setDate(d.getDate() + 1);
state.date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
await loadAvailability();
snapSelectionToFree();
rerender();
};
});
const start = qs("#bookingStart", root);
const end = qs("#bookingEnd", root);
const people = qs("#bookingPeople", root);
const paymentMethod = qs("#bookingPaymentMethod", root);
const dateInput = qs("#bookingDate", root);
const wantInvoice = qs("#wantInvoice", root);
const invoiceFields = qs("#invoiceFields", root);
if (start) {
start.onchange = () => {
const val = timeTextToMin(start.value);
if (val !== null) {
state.startMin = Math.max(minSelectableStartMin(), val);
if (state.endMin <= state.startMin) {
state.endMin = Math.min(DAY_END_MIN, state.startMin + Math.max(15, selectionDuration()));
}
snapSelectionToFree();
rerender();
}
};
}
if (end) {
end.onchange = () => {
const val = timeTextToMin(end.value);
if (val !== null) {
state.endMin = Math.max(minSelectableStartMin() + 15, val);
if (state.endMin <= state.startMin) state.endMin = Math.min(DAY_END_MIN, state.startMin + 15);
snapSelectionToFree();
rerender();
}
};
}
if (people) {
people.onchange = () => {
const cap = resourceCapacity();
const val = Number(people.value || 1);
state.peopleCount = Math.max(1, Math.min(cap, Number.isFinite(val) ? Math.floor(val) : 1));
rerender();
};
}
if (paymentMethod) {
paymentMethod.onchange = () => {
const value = String(paymentMethod.value || "ONLINE").toUpperCase();
state.paymentMethod = value === "CASH" ? "CASH" : "ONLINE";
};
}
if (wantInvoice) {
wantInvoice.onchange = () => {
state.wantInvoice = wantInvoice.checked;
if (invoiceFields) invoiceFields.classList.toggle("hide", !state.wantInvoice);
};
}
if (dateInput) {
dateInput.onchange = async () => {
if (!dateInput.value) return;
if (isPastDateKey(dateInput.value)) {
dateInput.value = state.date;
setMessage("Nie można rezerwować w przeszłości.", "error");
return;
}
state.date = dateInput.value;
await loadAvailability();
snapSelectionToFree();
rerender();
};
}
root.querySelectorAll("[data-action='check']").forEach((btn) => {
btn.onclick = handleCheckAvailability;
});
root.querySelectorAll("[data-action='send-details']").forEach((btn) => {
btn.onclick = handleSendDetails;
});
root.querySelectorAll("[data-action='pay-confirm']").forEach((btn) => {
btn.onclick = () => handlePayment("confirm");
});
root.querySelectorAll("[data-action='pay-fail']").forEach((btn) => {
btn.onclick = () => handlePayment("fail");
});
root.querySelectorAll("[data-action='pay-cash-confirm']").forEach((btn) => {
btn.onclick = () => handlePayment("cash");
});
root.querySelectorAll("[data-action='cancel-process']").forEach((btn) => {
btn.onclick = handleCancelProcess;
});
}
async function mount(root) {
state.mountedEl = root;
try {
normalizeSelectionToAllowedWindow();
await loadResources();
await loadAvailability();
rerender();
} catch (err) {
root.innerHTML = `<div class="booking-widget"><h3>Rezerwacje</h3><div class="booking-msg error">Nie udało się połączyć z backendem rezerwacji.</div></div>`;
}
}
function mountIfPresent() {
const root = document.getElementById("booking-root");
if (!root) return;
if (root.dataset.bookingMounted === "1") return;
root.dataset.bookingMounted = "1";
mount(root);
}
const observer = new MutationObserver(() => mountIfPresent());
observer.observe(document.documentElement, { childList: true, subtree: true });
window.addEventListener("DOMContentLoaded", mountIfPresent);
window.addEventListener("hashchange", () => {
setTimeout(mountIfPresent, 0);
});
})();