(() => { "use strict"; const fixture = new URLSearchParams(window.location.search).get("fixture") || ""; const form = document.querySelector("#franchise-form"); const formSteps = [...document.querySelectorAll("[data-step]")]; const indicators = [...document.querySelectorAll("[data-step-indicator]")]; const indicatorList = document.querySelector(".step-indicator"); const areaField = document.querySelector("#area-field"); const areaInput = document.querySelector("#exclusive-area"); const areaOutput = document.querySelector("#area-conversion"); const successPanel = document.querySelector("#inquiry-success"); const submitState = document.querySelector("#submit-state"); const submitButton = document.querySelector("#submit-inquiry"); let currentStep = 1; const markBrokenImage = (image) => { const shell = image.closest(".image-shell"); if (shell) shell.classList.add("is-broken"); }; document.querySelectorAll(".image-shell img").forEach((image) => { image.addEventListener("error", () => markBrokenImage(image)); if (image.complete && image.naturalWidth === 0) markBrokenImage(image); }); if (!form) return; const getRadioValue = (name) => form.querySelector(`input[name="${name}"]:checked`)?.value || ""; const getSelectValue = (name) => { const field = form.elements.namedItem(name); return field instanceof HTMLSelectElement ? field.value : ""; }; const getInputValue = (name) => { const field = form.elements.namedItem(name); return field instanceof HTMLInputElement ? field.value.trim() : ""; }; const setStep = (step) => { currentStep = step; formSteps.forEach((section) => section.classList.toggle("is-current", Number(section.dataset.step) === step)); indicators.forEach((item) => { const active = Number(item.dataset.stepIndicator) === step; item.classList.toggle("is-current", active); if (active) item.setAttribute("aria-current", "step"); else item.removeAttribute("aria-current"); }); const heading = form.querySelector(`[data-step="${step}"] h3`); if (heading instanceof HTMLElement) { heading.setAttribute("tabindex", "-1"); heading.focus({ preventScroll: true }); } }; const clearFieldError = (fieldId, errorId) => { const field = document.querySelector(`#${fieldId}`); const error = document.querySelector(`#${errorId}`); const wrapper = field?.closest(".form-field"); wrapper?.classList.remove("has-error"); field?.removeAttribute("aria-invalid"); field?.removeAttribute("aria-describedby"); if (error) error.textContent = ""; }; const setFieldError = (fieldId, errorId, message) => { const field = document.querySelector(`#${fieldId}`); const error = document.querySelector(`#${errorId}`); const wrapper = field?.closest(".form-field"); wrapper?.classList.add("has-error"); field?.setAttribute("aria-invalid", "true"); field?.setAttribute("aria-describedby", errorId); if (error) error.textContent = message; return field; }; const clearRadioError = (name, errorId) => { const radios = [...form.querySelectorAll(`input[name="${name}"]`)]; const wrapper = radios[0]?.closest(".form-field"); wrapper?.classList.remove("has-error"); radios.forEach((radio) => { radio.removeAttribute("aria-invalid"); radio.removeAttribute("aria-describedby"); }); const error = document.querySelector(`#${errorId}`); if (error) error.textContent = ""; }; const setRadioError = (name, errorId, message) => { const radios = [...form.querySelectorAll(`input[name="${name}"]`)]; const wrapper = radios[0]?.closest(".form-field"); wrapper?.classList.add("has-error"); radios.forEach((radio) => { radio.setAttribute("aria-invalid", "true"); radio.setAttribute("aria-describedby", errorId); }); const error = document.querySelector(`#${errorId}`); if (error) error.textContent = message; return radios[0]; }; const validateStepOne = () => { const invalid = []; clearFieldError("demo-name", "demo-name-error"); clearFieldError("region", "region-error"); clearRadioError("areaKind", "area-kind-error"); if (getInputValue("demoName").length < 2) { invalid.push(setFieldError("demo-name", "demo-name-error", "예시 호칭을 두 글자 이상 입력해 주세요.")); } if (!getSelectValue("region")) { invalid.push(setFieldError("region", "region-error", "희망 지역을 골라 주세요.")); } if (!getRadioValue("areaKind")) { invalid.push(setRadioError("areaKind", "area-kind-error", "희망 상권을 골라 주세요.")); } invalid[0]?.focus(); return invalid.length === 0; }; const requiresArea = () => ["협의 중", "보유함"].includes(getRadioValue("storePossession")); const updateAreaField = () => { const required = requiresArea(); if (areaField) areaField.hidden = !required; if (areaInput instanceof HTMLInputElement) { areaInput.required = required; if (!required) { areaInput.value = ""; clearFieldError("exclusive-area", "exclusive-area-error"); if (areaOutput) areaOutput.textContent = "면적을 입력하면 평으로도 보여 드립니다."; } } }; const updateAreaConversion = () => { if (!(areaInput instanceof HTMLInputElement) || !areaOutput) return; const squareMeters = Number(areaInput.value); if (!Number.isFinite(squareMeters) || squareMeters <= 0) { areaOutput.textContent = "면적을 입력하면 평으로도 보여 드립니다."; return; } const pyeong = Math.round((squareMeters / 3.3058) * 10) / 10; areaOutput.textContent = `${squareMeters.toLocaleString("ko-KR")}㎡ · 약 ${pyeong.toLocaleString("ko-KR")}평`; }; const validateStepTwo = () => { const invalid = []; clearRadioError("storePossession", "store-possession-error"); clearFieldError("exclusive-area", "exclusive-area-error"); clearFieldError("opening-window", "opening-window-error"); clearRadioError("experience", "experience-error"); if (!getRadioValue("storePossession")) { invalid.push(setRadioError("storePossession", "store-possession-error", "점포 상태를 골라 주세요.")); } if (requiresArea()) { const value = Number(getInputValue("exclusiveArea")); if (!Number.isFinite(value) || value <= 0) { invalid.push(setFieldError("exclusive-area", "exclusive-area-error", "면적은 0보다 큰 숫자로 입력해 주세요.")); } } if (!getSelectValue("openingWindow")) { invalid.push(setFieldError("opening-window", "opening-window-error", "희망 시작 시기를 골라 주세요.")); } if (!getRadioValue("experience")) { invalid.push(setRadioError("experience", "experience-error", "운영 경험 여부를 골라 주세요.")); } invalid[0]?.focus(); return invalid.length === 0; }; const reviewValues = () => { const squareMeters = Number(getInputValue("exclusiveArea")); const areaLabel = requiresArea() && squareMeters > 0 ? `${squareMeters.toLocaleString("ko-KR")}㎡ · 약 ${(Math.round((squareMeters / 3.3058) * 10) / 10).toLocaleString("ko-KR")}평` : "해당 없음"; const values = { demoName: getInputValue("demoName"), region: getSelectValue("region"), areaKind: getRadioValue("areaKind"), storePossession: getRadioValue("storePossession"), exclusiveArea: areaLabel, openingWindow: getSelectValue("openingWindow"), experience: getRadioValue("experience"), }; Object.entries(values).forEach(([key, value]) => { const target = document.querySelector(`[data-review="${key}"]`); if (target) target.textContent = value || "—"; }); }; form.querySelectorAll("[data-next]").forEach((button) => { button.addEventListener("click", () => { if (currentStep === 1 && validateStepOne()) setStep(2); else if (currentStep === 2 && validateStepTwo()) { reviewValues(); setStep(3); } }); }); form.querySelectorAll("[data-back]").forEach((button) => { button.addEventListener("click", () => setStep(Math.max(1, currentStep - 1))); }); form.querySelectorAll("input[name='storePossession']").forEach((input) => { input.addEventListener("change", updateAreaField); }); areaInput?.addEventListener("input", updateAreaConversion); form.addEventListener("input", (event) => { const field = event.target; if (!(field instanceof HTMLElement) || !field.id) return; const errorId = `${field.id}-error`; if (document.querySelector(`#${errorId}`)) clearFieldError(field.id, errorId); }); form.addEventListener("change", (event) => { const field = event.target; if (!(field instanceof HTMLInputElement)) return; if (field.type === "radio") { const names = { areaKind: "area-kind-error", storePossession: "store-possession-error", experience: "experience-error", }; if (names[field.name]) clearRadioError(field.name, names[field.name]); } }); form.addEventListener("submit", (event) => { event.preventDefault(); const consent = document.querySelector("#demo-consent"); const consentError = document.querySelector("#demo-consent-error"); if (consent instanceof HTMLInputElement) { consent.removeAttribute("aria-invalid"); consent.removeAttribute("aria-describedby"); } if (consentError) consentError.textContent = ""; if (!(consent instanceof HTMLInputElement) || !consent.checked) { consent?.setAttribute("aria-invalid", "true"); consent?.setAttribute("aria-describedby", "demo-consent-error"); if (consentError) consentError.textContent = "문의 체험 안내를 확인해 주세요."; consent?.focus(); return; } if (submitButton instanceof HTMLButtonElement) { submitButton.disabled = true; submitButton.textContent = "화면 상태 확인 중"; } if (submitState) submitState.textContent = ""; window.setTimeout(() => { if (fixture === "inquiry-fail") { if (submitButton instanceof HTMLButtonElement) { submitButton.disabled = false; submitButton.textContent = "다시 시도하기"; } if (submitState) { submitState.textContent = "지금은 문의 준비 상태를 바꾸지 못했어요. 입력 내용은 화면에 그대로 두었습니다."; } return; } form.hidden = true; if (indicatorList) indicatorList.hidden = true; if (successPanel) { successPanel.hidden = false; successPanel.focus(); } if (submitButton instanceof HTMLButtonElement) { submitButton.disabled = false; submitButton.textContent = "문의 준비 마치기"; } }, 450); }); document.querySelector("#review-again")?.addEventListener("click", () => { if (successPanel) successPanel.hidden = true; form.hidden = false; if (indicatorList) indicatorList.hidden = false; setStep(3); }); document.querySelector("#restart-form")?.addEventListener("click", () => { form.reset(); form.hidden = false; if (successPanel) successPanel.hidden = true; if (indicatorList) indicatorList.hidden = false; if (submitState) submitState.textContent = ""; document.querySelectorAll(".field-error").forEach((error) => { error.textContent = ""; }); document.querySelectorAll(".has-error").forEach((field) => field.classList.remove("has-error")); updateAreaField(); setStep(1); }); updateAreaField(); setStep(1); })();