vinindkoeb/app/static/site.js
carsten cae8decd21 Opgave 8c: bekræftelsesside efter bestilling + responsivt layout-fix
Baseret på brugerens gennemgang af siden i browser:
- Ved succesfuld bestilling omdirigeres brugeren nu til en dedikeret
  GET /bestilling-modtaget i stedet for en inline besked, der skjulte
  vinkataloget — brugeren vil fortsat kunne se hvad de har bestilt.
  Siden gør det eksplicit at bestillingen først er gyldig ved
  modtaget betaling, og linker tilbage til bestillingssiden.
- Rettet et CSS-layoutbug på smalle skærme: flex-basis (sat til
  600px/260px til brug for bredde i to-kolonne-layoutet) blev
  fejlagtigt fortolket som højde, når layoutet skiftede til
  flex-direction: column under 800px, hvilket gav et stort tomt
  mellemrum mellem vinkatalog og sidebar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 23:51:41 +02:00

124 lines
4.2 KiB
JavaScript

(function () {
function formatAmount(n) {
return n.toFixed(2);
}
function showMessage(el, text, kind) {
el.textContent = text;
el.className = "form-message " + kind;
el.hidden = false;
}
async function extractErrorDetail(response) {
try {
const body = await response.json();
if (typeof body.detail === "string") return body.detail;
if (Array.isArray(body.detail)) {
return body.detail.map((e) => e.msg || JSON.stringify(e)).join(", ");
}
return JSON.stringify(body.detail);
} catch (err) {
return "Der skete en fejl. Prøv igen.";
}
}
const orderForm = document.getElementById("order-form");
if (orderForm) {
const qtyInputs = Array.from(orderForm.querySelectorAll(".qty-input"));
const totalEurEl = document.getElementById("total-eur");
const totalDkkEl = document.getElementById("total-dkk");
const rateAttr = orderForm.dataset.eurDkkRate;
const rate = rateAttr ? parseFloat(rateAttr) : null;
function recalculate() {
let totalEur = 0;
qtyInputs.forEach((input) => {
const qty = parseInt(input.value, 10) || 0;
const price = parseFloat(input.dataset.price) || 0;
const rowTotal = qty * price;
totalEur += rowTotal;
const cell = orderForm.querySelector(
'[data-row-total-for="' + input.dataset.offeringId + '"]'
);
if (cell) cell.textContent = formatAmount(rowTotal);
});
if (totalEurEl) totalEurEl.textContent = formatAmount(totalEur);
if (totalDkkEl && rate) totalDkkEl.textContent = formatAmount(totalEur * rate);
}
qtyInputs.forEach((input) => input.addEventListener("input", recalculate));
recalculate();
orderForm.addEventListener("submit", async function (event) {
event.preventDefault();
const messageEl = document.getElementById("order-message");
const orderLines = qtyInputs
.map((input) => ({
wine_offering_id: parseInt(input.dataset.offeringId, 10),
quantity: parseInt(input.value, 10) || 0,
}))
.filter((line) => line.quantity > 0);
if (orderLines.length === 0) {
showMessage(messageEl, "Vælg mindst én vin med et antal større end 0.", "error");
return;
}
const payload = {
participant: {
name: orderForm.elements["name"].value,
email: orderForm.elements["email"].value,
phone: orderForm.elements["phone"].value,
is_active: orderForm.elements["is_active"].checked,
},
order_lines: orderLines,
};
try {
const response = await fetch("/public/orders", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (response.ok) {
window.location.href = "/bestilling-modtaget";
return;
} else {
showMessage(messageEl, await extractErrorDetail(response), "error");
}
} catch (err) {
showMessage(messageEl, "Kunne ikke sende bestillingen. Tjek din internetforbindelse og prøv igen.", "error");
}
});
}
const signupForm = document.getElementById("signup-form");
if (signupForm) {
signupForm.addEventListener("submit", async function (event) {
event.preventDefault();
const messageEl = document.getElementById("signup-message");
const payload = {
name: signupForm.elements["name"].value,
email: signupForm.elements["email"].value,
};
try {
const response = await fetch("/public/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (response.ok) {
signupForm.querySelectorAll("label, button[type=submit]").forEach((el) => {
el.hidden = true;
});
showMessage(messageEl, "Tak! Du er nu tilmeldt.", "success");
} else {
showMessage(messageEl, await extractErrorDetail(response), "error");
}
} catch (err) {
showMessage(messageEl, "Kunne ikke sende tilmeldingen. Tjek din internetforbindelse og prøv igen.", "error");
}
});
}
})();