const stepCount = 6; const catalogs = { systems: { mono_230: { name: "System 1-fazowy 230V", base: 159, meter: 89 }, tri_230: { name: "System 3-fazowy 230V", base: 249, meter: 129 }, magnetic_48: { name: "System magnetyczny 48V", base: 399, meter: 179 } }, layouts: { line: { name: "Układ liniowy", surcharge: 0 }, l_shape: { name: "Układ L", surcharge: 59 }, u_shape: { name: "Układ U", surcharge: 99 }, rectangle: { name: "Układ prostokątny", surcharge: 149 } }, colors: { black: { name: "Czarny mat", surcharge: 0, line: "#252627" }, white: { name: "Biały mat", surcharge: 0, line: "#f7f6f8" }, champagne: { name: "Champagne", surcharge: 40, line: "#d7bd8b" } }, temperatures: { "3000": "3000K ciepła", "4000": "4000K neutralna", cct: "CCT regulowana" } }; const state = { step: 1, system: null, layout: null, trackLength: 4, roomHeight: 2.8, trackColor: null, temperature: null, fixtures: {}, accessories: new Set() }; const fixtureGrid = document.getElementById("fixtureGrid"); const accessoryList = document.getElementById("accessoryList"); const summaryList = document.getElementById("summaryList"); const summaryTotal = document.getElementById("summaryTotal"); const finalSummary = document.getElementById("finalSummary"); const previewCanvas = document.getElementById("previewCanvas"); const trackLengthInput = document.getElementById("trackLength"); const trackLengthOut = document.getElementById("trackLengthOut"); const roomHeightInput = document.getElementById("roomHeight"); const roomHeightOut = document.getElementById("roomHeightOut"); const prevBtn = document.getElementById("prevBtn"); const nextBtn = document.getElementById("nextBtn"); const toast = document.getElementById("toast"); const fixtures = buildFixtures(); const accessories = buildAccessories(); init(); function init() { renderFixtures(); renderAccessories(); bindBaseEvents(); syncInputs(); updateUI(); } function buildFixtures() { const names = [ "Reflektor Punto", "Spot Slim Tube", "Lampa liniowa Aero", "Reflektor Move Pro", "Tubus Mono Flex", "Panel Linear Edge", "Oprawa Twist Mini" ]; return names.slice(0, 6).map((name, i) => ({ id: `fx_${i + 1}`, code: `LT-FX-${1200 + i}`, name, price: randomInt(85, 279) })); } function buildAccessories() { const names = [ "Zasilacz końcowy", "Łącznik prosty", "Łącznik narożny L", "Maskownica zasilania", "Pilot CCT", "Zawiesie sufitowe 1 m" ]; return names.map((name, i) => ({ id: `acc_${i + 1}`, name, price: randomInt(24, 119) })); } function bindBaseEvents() { document.addEventListener("change", (event) => { const target = event.target; if (target.name === "system") { state.system = target.value; } if (target.name === "layout") { state.layout = target.value; } if (target.name === "trackColor") { state.trackColor = target.value; } if (target.name === "temperature") { state.temperature = target.value; } if (target.matches("[data-accessory]")) { if (target.checked) { state.accessories.add(target.value); } else { state.accessories.delete(target.value); } } updateUI(); }); fixtureGrid.addEventListener("click", (event) => { const button = event.target.closest("button[data-fixture-id]"); if (!button) { return; } const id = button.dataset.fixtureId; const action = button.dataset.action; const currentQty = state.fixtures[id] || 0; const nextQty = action === "inc" ? currentQty + 1 : Math.max(0, currentQty - 1); state.fixtures[id] = nextQty; updateUI(); }); trackLengthInput.addEventListener("input", () => { state.trackLength = Number(trackLengthInput.value); syncInputs(); updateUI(); }); roomHeightInput.addEventListener("input", () => { const clamped = Math.min(4.5, Math.max(2.2, Number(roomHeightInput.value || 2.8))); state.roomHeight = Number(clamped.toFixed(1)); syncInputs(); }); prevBtn.addEventListener("click", () => { if (state.step > 1) { state.step -= 1; updateUI(); } }); nextBtn.addEventListener("click", () => { if (state.step < stepCount) { const valid = validateCurrentStep(); if (!valid.ok) { showToast(valid.message); return; } state.step += 1; updateUI(); return; } showToast("Makieta: zestaw został przykładowo dodany do koszyka."); }); document.querySelectorAll("[data-goto-step]").forEach((button) => { button.addEventListener("click", () => { const goto = Number(button.dataset.gotoStep); if (goto <= state.step || goto === state.step + 1) { if (goto > state.step) { const valid = validateCurrentStep(); if (!valid.ok) { showToast(valid.message); return; } } state.step = goto; updateUI(); } }); }); } function renderFixtures() { fixtureGrid.innerHTML = fixtures .map( (fixture) => `
${fixture.code}
${fixture.name}
${formatPLN(fixture.price)}
${state.fixtures[fixture.id] || 0}
` ) .join(""); } function renderAccessories() { accessoryList.innerHTML = accessories .map( (item) => `
${formatPLN(item.price)}
` ) .join(""); } function updateUI() { updateStepPanels(); updateStepButtons(); updateActions(); renderFixtures(); refreshAccessoryChecks(); renderSummary(); renderFinalSummary(); renderPreview(); } function updateStepPanels() { document.querySelectorAll(".step-panel").forEach((panel) => { panel.classList.toggle("is-active", Number(panel.dataset.step) === state.step); }); } function updateStepButtons() { document.querySelectorAll(".step-item").forEach((button) => { const step = Number(button.dataset.gotoStep); button.classList.toggle("is-active", step === state.step); button.classList.toggle("is-done", step < state.step); }); } function updateActions() { prevBtn.disabled = state.step === 1; nextBtn.textContent = state.step === stepCount ? "Dodaj do koszyka" : "Dalej"; } function renderSummary() { const breakdown = getBreakdown(); summaryList.innerHTML = breakdown.items .map((item) => `
  • ${item.label}${formatPLN(item.value)}
  • `) .join(""); summaryTotal.textContent = formatPLN(breakdown.total); } function renderFinalSummary() { const breakdown = getBreakdown(); finalSummary.innerHTML = ` `; } function refreshAccessoryChecks() { document.querySelectorAll("[data-accessory]").forEach((input) => { input.checked = state.accessories.has(input.value); }); } function getBreakdown() { const items = []; let total = 0; if (state.system) { const system = catalogs.systems[state.system]; const value = system.base; items.push({ label: system.name, value }); total += value; } if (state.layout) { const layout = catalogs.layouts[state.layout]; if (layout.surcharge > 0) { items.push({ label: layout.name, value: layout.surcharge }); total += layout.surcharge; } } if (state.system) { const system = catalogs.systems[state.system]; const trackValue = Math.round(state.trackLength * system.meter); items.push({ label: `Szyny ${state.trackLength.toFixed(1)} m`, value: trackValue }); total += trackValue; } if (state.trackColor) { const color = catalogs.colors[state.trackColor]; if (color.surcharge > 0) { items.push({ label: `Wykończenie ${color.name}`, value: color.surcharge }); total += color.surcharge; } } fixtures.forEach((fixture) => { const qty = state.fixtures[fixture.id] || 0; if (qty > 0) { const value = qty * fixture.price; items.push({ label: `${fixture.name} x${qty}`, value }); total += value; } }); accessories.forEach((acc) => { if (state.accessories.has(acc.id)) { items.push({ label: acc.name, value: acc.price }); total += acc.price; } }); if (items.length === 0) { items.push({ label: "Brak wybranych pozycji", value: 0 }); } return { items, total }; } function renderPreview() { const colorToken = state.trackColor ? catalogs.colors[state.trackColor].line : "#f7f6f8"; const spotCount = Math.max(1, Object.values(state.fixtures).reduce((sum, qty) => sum + qty, 0)); const points = getPreviewPoints(state.layout || "line"); const circles = Array.from({ length: spotCount }, (_, i) => { const point = points[i % points.length]; const tone = getPreviewLightTone(state.temperature, i); return ``; }).join(""); const lines = getPreviewPath(state.layout || "line", colorToken); previewCanvas.innerHTML = ` `; } function getPreviewPath(layout, color) { const stroke = ``; if (layout === "l_shape") { return `${stroke}`; } if (layout === "u_shape") { return `${stroke}`; } if (layout === "rectangle") { return `${stroke}`; } return `${stroke}`; } function getPreviewPoints(layout) { if (layout === "l_shape") { return [ { x: 48, y: 54 }, { x: 48, y: 82 }, { x: 48, y: 112 }, { x: 98, y: 130 }, { x: 150, y: 130 }, { x: 205, y: 130 }, { x: 260, y: 130 } ]; } if (layout === "u_shape") { return [ { x: 45, y: 55 }, { x: 45, y: 100 }, { x: 95, y: 130 }, { x: 145, y: 130 }, { x: 195, y: 130 }, { x: 245, y: 130 }, { x: 300, y: 102 }, { x: 300, y: 58 } ]; } if (layout === "rectangle") { return [ { x: 76, y: 40 }, { x: 128, y: 40 }, { x: 196, y: 40 }, { x: 262, y: 40 }, { x: 300, y: 76 }, { x: 300, y: 114 }, { x: 250, y: 130 }, { x: 175, y: 130 }, { x: 100, y: 130 }, { x: 60, y: 96 } ]; } return [ { x: 64, y: 86 }, { x: 104, y: 86 }, { x: 146, y: 86 }, { x: 188, y: 86 }, { x: 230, y: 86 }, { x: 272, y: 86 }, { x: 312, y: 86 } ]; } function getPreviewLightTone(temperature, index) { if (temperature === "3000") { return { fill: "#ffb766", stroke: "#ffd4a2" }; } if (temperature === "4000") { return { fill: "#fff3d2", stroke: "#fff9ea" }; } if (temperature === "cct") { return index % 2 === 0 ? { fill: "#ffbc77", stroke: "#ffe0b5" } : { fill: "#c8e8ff", stroke: "#e9f6ff" }; } return { fill: "#f7614d", stroke: "#ffd2cc" }; } function validateCurrentStep() { if (state.step === 1 && !state.system) { return { ok: false, message: "Wybierz system szynowy, aby przejść dalej." }; } if (state.step === 2 && !state.layout) { return { ok: false, message: "Wybierz kształt konfiguracji." }; } if (state.step === 3 && (!state.trackColor || !state.temperature)) { return { ok: false, message: "Uzupełnij kolor szyny i temperaturę barwową." }; } return { ok: true }; } function syncInputs() { trackLengthOut.textContent = state.trackLength.toFixed(1); roomHeightInput.value = state.roomHeight.toFixed(1); roomHeightOut.textContent = state.roomHeight.toFixed(1); } function showToast(text) { toast.textContent = text; toast.classList.add("is-visible"); clearTimeout(showToast.timer); showToast.timer = setTimeout(() => { toast.classList.remove("is-visible"); }, 2100); } function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function formatPLN(value) { return `${value.toLocaleString("pl-PL")} zł`; }