(() => { const { pageHelper, EventManager, utils, actions } = ShopbySkin; const API_BASE_URL = `${DSDOENV.ApiConfig.DSDO_API}/api/v1/on-site-exams`; const searchParams = new URLSearchParams(location.search); const productNo = searchParams.get("productNo"); const channelType = searchParams.get("channelType"); const sortType = searchParams.get("sortType"); const cartAction = actions.getCartAction(); let examInfo = null; const memberNo = document.cookie.match(/memberNo=([^;]+)/)?.[1]; const { productDetailHelper } = pageHelper; const { initialize } = productDetailHelper(); initialize({ productNo, channelType, sortType, cartAction, }); const checkAvailability = async () => { try { const response = await fetch(`${API_BASE_URL}/applications/availability?mockExamId=${examInfo.mockExamId}`); if(!response.ok) { throw new Error('학교 응시 모의고사 신청 가능 여부 조회에 실패했습니다.'); } const result = await response.json(); if(!result.data) { throw new Error('학교 응시 모의고사 신청 가능 여부를 찾을 수 없습니다.'); } return !result.data?.closed; } catch(e) { console.error(e); return false; } } const redirectToMainPage = () => { EventManager.fire("MODAL_ALERT_OPEN", { message: "유효하지 않은 상품입니다.", onClose: () => { location.href = '/'; }, }); } const getOnSiteExamInfo = async (productNo) => { if(!productNo) { redirectToMainPage(); return; } try { const response = await fetch(`${API_BASE_URL}/product-no/${productNo}`); if(!response.ok) { throw new Error('학교 응시 모의고사 정보 조회에 실패했습니다.'); } const result = await response.json(); if(!result.data?.length) { throw new Error('학교 응시 모의고사 정보를 찾을 수 없습니다.'); } examInfo = result.data[0] sessionStorage.setItem('on-site-exam-info', JSON.stringify(result.data[0])); // 이후 페이지에서 사용하기 위해 세션 스토리지 저장 } catch(e) { console.error(e); redirectToMainPage(); } } const getSubjectOptions = async () => { try { const response = await fetch(`${API_BASE_URL}/subjects`); if(!response.ok) { throw new Error('학교 응시 선택 과목 목록 조회에 실패했습니다.'); } const result = await response.json(); if(!result.data) { throw new Error('학교 응시 선택 과목 목록을 찾을 수 없습니다.'); } return result.data; } catch(e) { console.error(e); return null; } } const redirectLoginPage = () => { const nextUrl = `${location.origin}/pages/sign-in/sign-in.html`; location.href = `${nextUrl}?from=${encodeURIComponent(location.href)}`; }; const productLike = (productNo, { onSuccess }) => { if (!utils.isSignIn()) { EventManager.fire("MODAL_ALERT_OPEN", { message: "로그인 후 이용할 수 있습니다.", onClose: redirectLoginPage, }); return; } ShopbySkin.actions.toggleLikeStatus({ productNo, onSuccess, }); }; const checkIfAlreadyPurchased = async () => { if(!examInfo || !memberNo) return; let examMonthPrefix = 'october'; // default 10월 if(new Date(examInfo.examDate).getMonth() + 1 === 10) { examMonthPrefix = 'october'; } try { const response = await fetch( `${API_BASE_URL}/orders/${examMonthPrefix}-mock-exam/exists?memberNo=${memberNo}` ); if(!response.ok) { throw new Error('더프 모의고사 기구매 이력 조회에 실패했습니다.'); } const result = await response.json(); if(!result.data) { throw new Error('더프 모의고사 기구매 이력을 찾을 수 없습니다.'); } return result.data.exists; } catch(e) { console.error(e); redirectToMainPage(); return null; } } const checkIfApplicable = async () => { if(!examInfo || !memberNo) return; try { const response = await fetch( `${API_BASE_URL}/applications/status?mockExamId=${examInfo.mockExamId}&memberNo=${memberNo}` ); if(!response.ok) { throw new Error('학교 응시 신청 이력 조회에 실패했습니다.'); } const result = await response.json(); if(!result.data) { throw new Error('학교 응시 신청 이력을 찾을 수 없습니다.'); } return result.data.apply; } catch(e) { console.error(e); redirectToMainPage(); return null; } } const fillFormWithUserInfo = async (formEl, profile) => { const { data: extraInfoConfig } = await actions.queryConfigMemberExtraInfo(); const schoolNameNo = extraInfoConfig?.extraInfoContents?.find(({ extraInfoName }) => extraInfoName === "소속학교(학원)").extraInfoNo; if(!formEl || !extraInfoConfig) return; const userInfo = { name: profile.memberName, birthday: profile.birthday.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'), gender: profile.sex === "F" ? 2 : 1, phone: profile.mobileNo, academyName: profile.extraInfo.find(({ extraInfoNo }) => extraInfoNo === schoolNameNo)?.extraInfoOptionTextContent || "", } Object.entries(userInfo).forEach(([key, value]) => { const field = formEl.elements[key]; if(!field) return; field.value = value; field.removeAttribute("data-invalid"); }) const maleCheckbox = formEl.querySelector("#male"); const femaleCheckbox = formEl.querySelector("#female"); maleCheckbox.checked = userInfo.gender === 1; femaleCheckbox.checked = userInfo.gender === 2; } const assignClass = (formEl, inquiryOptions, subject1Value, subject2Value) => { const scienceOptions = inquiryOptions.science.map(({ name }) => name); const socialStudiesOptions = inquiryOptions.socialStudies.map(({ name }) => name); const classLabelEl = formEl.querySelector(".subject_info .class_label"); classLabelEl.style.display = "flex"; if(scienceOptions.includes(subject1Value) && scienceOptions.includes(subject2Value)) { classLabelEl.textContent = "과탐반"; } else if(socialStudiesOptions.includes(subject1Value) && socialStudiesOptions.includes(subject2Value)) { classLabelEl.textContent = "사탐반"; } else if(!!subject1Value & !!subject2Value) { classLabelEl.textContent = "교차반"; } else { classLabelEl.style.display = "none"; } } // 응시 정보 폼 세팅 const setFormRules = async (formEl) => { const { name, gender, phone, birthday, studentGrade, academyName, korean, math, subject1, subject2, noticeAgreed, cancelAgreed } = formEl.elements; Array.from(formEl.elements).forEach((el) => { el.addEventListener("change", (e) => { if(e.target.type === "hidden") return; const target = e.target; const isInvalid = target.getAttribute("data-invalid") === "true"; if(isInvalid) { target.removeAttribute("data-invalid"); } else { if(target.type === "checkbox") { const hiddenInput = target.closest(".radio_group")?.querySelector("input[type=hidden][data-invalid='true']"); if(hiddenInput) { hiddenInput.removeAttribute("data-invalid"); } } } }) }) birthday.max = new Date().toISOString().split("T")[0]; name.addEventListener("input", (e) => { let value = e.target.value.replace(/[^가-힣ㄱ-ㅎㅏ-ㅣ\u318D\u119E\u11A2\u2022\u00B7]/g, ''); if (value.length > 5) { value = value.slice(0, 5); } e.target.value = value; }); const genderCheckboxes = gender.parentElement.querySelectorAll("input[type=checkbox]"); genderCheckboxes.forEach((el, index) => { const theOther = genderCheckboxes[index === 0 ? 1 : 0]; el.addEventListener("click", (event) => { const checked = event.target.checked; const { value } = event.target; if(checked) { gender.value = value; theOther.checked = !checked; } else { event.preventDefault(); } }); }); phone.addEventListener("input", (e) => { let value = e.target.value.replace(/[^0-9]/g, ''); if (value.length > 11) { value = value.slice(0, 11); } e.target.value = value; }); const studentGradeCheckboxes = studentGrade.parentElement.querySelectorAll("input[type=checkbox]"); studentGradeCheckboxes.forEach((el, index) => { const theOthers = Array.from(studentGradeCheckboxes).filter((_, i) => i !== index); el.addEventListener("click", (event) => { const checked = event.target.checked; const { value } = event.target; if(checked) { studentGrade.value = value; theOthers.forEach((el) => el.checked = false); } else { event.preventDefault(); } }); }); // 과목 선택 const subjectOptions = await getSubjectOptions(); if(subjectOptions) { korean.replaceChildren(...[korean.children[0], ...subjectOptions.korean.map((subject) => { return new Option(subject.label, subject.name); })]); math.replaceChildren(...[math.children[0], ...subjectOptions.math.map((subject) => { return new Option(subject.label, subject.name); })]); const inquiryOptions = [...subjectOptions.inquiry.science, ...subjectOptions.inquiry.socialStudies]; subject1.replaceChildren(...[subject1.children[0], ...inquiryOptions.map((subject) => { return new Option(subject.label, subject.name); })]); subject2.replaceChildren(...[subject2.children[0], ...inquiryOptions.map((subject) => { return new Option(subject.label, subject.name); })]); const subjects = formEl.querySelectorAll(".subject_info select"); subjects.forEach((el) => { el.addEventListener("click", (event) => { const isOpen = event.target.getAttribute("data-open") === "true"; event.target.setAttribute("data-open", !isOpen); }) el.addEventListener("blur", (event) => { event.target.setAttribute("data-open", false); }); }); subject1.addEventListener("change", (event) => { const subject1Value = event.target.value; subject2.disabled = !subject1Value; const subject2Options = Array.from(subject2.children); subject2Options.forEach((option) => { option.disabled = subject1Value === option.value; }); assignClass(formEl, subjectOptions.inquiry, subject1Value, subject2.value); }); subject2.addEventListener("change", (event) => { const subject2Value = event.target.value; const subject1Options = Array.from(subject1.children); subject1Options.forEach((option) => { option.disabled = subject2Value === option.value; }); assignClass(formEl, subjectOptions.inquiry, subject1.value, subject2Value); }) } // 기존 데이터 복구 후 세션 스토리지 삭제 처리 const previousFormData = JSON.parse(sessionStorage.getItem("application-info")); if(previousFormData?.isEdit) { birthday.value = previousFormData?.birthday || ""; name.value = previousFormData?.name || ""; phone.value = previousFormData?.phone?.replaceAll("-", "") || ""; if(previousFormData?.gender) { gender.value = previousFormData.gender; const check = formEl.querySelector(`input[type=checkbox][value='${previousFormData.gender}']`); if(check) { check.checked = true; } } if(previousFormData?.studentGrade) { studentGrade.value = previousFormData.studentGrade; const check = formEl.querySelector(`input[type=checkbox][value='${previousFormData.studentGrade}']`); if(check) { check.checked = true; } } academyName.value = previousFormData?.academyName || ""; korean.value = previousFormData?.korean || ""; math.value = previousFormData?.math || ""; subject1.value = previousFormData?.subject1 || ""; subject2.value = previousFormData?.subject2 || ""; subject2.disabled = !subject1.value; const subject2Options = Array.from(subject2.children); subject2Options.forEach((option) => { option.disabled = subject1.value === option.value; }); assignClass(formEl, subjectOptions.inquiry, subject1.value, subject2.value); noticeAgreed.checked = true; cancelAgreed.checked = true; } sessionStorage.removeItem("application-info"); } // 폼 유효성 검사 const validateForm = (formEl) => { if(!formEl) return; const elements = Array.from(formEl.elements); elements.forEach((el) => { const isValid = el.required ? el.type === "checkbox" ? el.checked : !!el.value : true; if(!isValid) { el.setAttribute('data-invalid', 'true'); } }) return elements.every((el) => { return el.required ? el.type === "checkbox" ? el.checked : !!el.value : true; }); } const ACTION_HANDLER_MAP = { COUPON_DOWNLOAD: () => { if (!utils.isSignIn()) { ShopbySkin.EventManager.fire("MODAL_ALERT_OPEN", { message: "로그인하셔야 본 서비스를 이용하실 수 있습니다", onClose: redirectLoginPage, }); } ShopbySkin.EventManager.fire("OPEN_LAYER_MODAL", { title: "쿠폰 다운받기", name: "coupon-download", data: { productNo, channelType: utils.getChannelType(), }, isFull: false, onClose: () => null, }); }, // 구매하기 PRODUCT_ORDER: async () => { if(!examInfo) return; // 마감 여부 검사 const availability = await checkAvailability(); if(!availability) { EventManager.fire("MODAL_ALERT_OPEN", { message: "THE PREMIUM 수능 리허설 신청이 마감되었습니다.", onClose: () => { location.href = "/"; } }); return; } // 수능 리허설 상품 구매 이력 검사 const isApplicable = await checkIfApplicable(); if(!isApplicable) { EventManager.fire("MODAL_ALERT_OPEN", { message: "10월 THE PREMIUM 수능 리허설 상품을 구매한
" + "이력이 있어 구입이 불가합니다.", }); return; } // 더프 기구매 상품 구매 이력 검사 const isAlreadyPurchased = await checkIfAlreadyPurchased(); if(!isAlreadyPurchased) { if(examInfo.onSiteExamProductNo2 === Number(productNo)) { // 응시료 EventManager.fire("MODAL_CONFIRM_OPEN", { message: "10월 THE PREMIUM 구매자만 해당 상품을 구매할 수 있습니다.
구매 가능한 THE PREMIUM 수능 리허설 상품으로 이동하시겠습니까?", confirmLabel: "구매 가능 상품 확인", cancelLabel: "닫기", onConfirm: () => { window.location.href = `/pages/rehearsal/product-detail.html?productNo=${examInfo.onSiteExamProductNo1}`; }, }); return; } } // 응시 정보 입력 유효성 검사 const formEl = document.querySelector("#application-form"); const isValid = validateForm(formEl); if(!isValid) { EventManager.fire("MODAL_ALERT_OPEN", { message: "필수 응시 정보를
입력/선택해 주세요.", }); return; } // 응시 정보 확인 모달 EventManager.fire("OPEN_LAYER_MODAL", { isFull : false, name : "application-confirm-modal", visibleCloseBtn: false, confirmLabel: "제출", cancelLabel: "응시정보 수정", data: { mockExamId: examInfo.mockExamId, name: formEl.name.value, memberNo: memberNo, gender: formEl.gender.value, birthday: formEl.birthday.value, phone: formEl.phone.value.length === 10 ? formEl.phone.value.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3') : formEl.phone.value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'), productNo: productNo, korean: formEl.korean.value, koreanName: formEl.korean.children[formEl.korean.selectedIndex].text, math: formEl.math.value, mathName: formEl.math.children[formEl.math.selectedIndex].text, subject1: formEl.subject1.value, subject1Name: formEl.subject1.children[formEl.subject1.selectedIndex].text, subject2: formEl.subject2.value, subject2Name: formEl.subject2.children[formEl.subject2.selectedIndex].text, studentGrade: formEl.studentGrade.value, studentGradeName: formEl.querySelector(`input[type="checkbox"][value="${formEl.studentGrade.value}"]`).getAttribute("data-label"), academyName: formEl.academyName.value, noticeAgreed: formEl.noticeAgreed.checked ? "Y" : "N", cancelAgreed: formEl.cancelAgreed.checked ? "Y" : "N" }, onConfirm: async () => { const { isSignedIn } = utils; if (isSignedIn()) { const { requestOrderSheet } = pageHelper.productDetailHelper(); const { orderSheetNo } = await requestOrderSheet(); const orderSheetNoURL = `${ location.origin }/pages/order/order-sheet-form.html?ordersheetNo=${orderSheetNo ?? 0}`; window.location.href = orderSheetNoURL; } else { const { requestOrderSheet } = pageHelper.productDetailHelper(); const { orderSheetNo } = await requestOrderSheet(); const orderSheetNoURL = `${ location.origin }/pages/order/order-sheet-form.html?ordersheetNo=${orderSheetNo ?? 0}`; window.location.href = orderSheetNoURL; } } }); }, // 관심상품 등록 PRODUCT_LIKE: (event) => { event.preventDefault(); const { target } = event; productLike(productNo, { onSuccess: (isLiked) => { const el = target.closest("[type=button]"); const messageEl = document.querySelector( '[shopby-element="like-action-message"]' ); messageEl.classList.remove("like-message"); isLiked ? EventManager.fire("MODAL_ALERT_OPEN", { message: "

상품이 관심상품 리스트에 추가되었습니다.

", }) : EventManager.fire("MODAL_ALERT_OPEN", { message: "

상품이 관심상품 리스트에서
제거되었습니다.

", }); messageEl.classList.add("like-message"); if (isLiked) { el.classList.add("is-active"); return; } el.classList.remove("is-active"); }, }); }, SEARCH_SCHOOL: () => { EventManager.fire("OPEN_LAYER_MODAL", { isFull : false, name : "search-class-modal", }); }, OPEN_APPLICATION_NOTICE: () => { EventManager.fire("OPEN_LAYER_MODAL", { isFull : false, name : "rehearsal-guidelines", }); }, OPEN_CANCEL_POLICY: () => { EventManager.fire("OPEN_LAYER_MODAL", { isFull : false, name : "rehearsal-refund", }); }, }; getOnSiteExamInfo(productNo); document .querySelector("custom-product-summary") ?.addEventListener("click", ({ target }) => { ACTION_HANDLER_MAP[target.getAttribute("shopby-action")]?.(); }); document .querySelector("custom-product-purchase") ?.addEventListener("click", (event) => { ACTION_HANDLER_MAP[event.target.getAttribute("shopby-action")]?.(event); }); // 소속 학교 검색 버튼 document.querySelector("button[shopby-action=SEARCH_SCHOOL]").addEventListener("click", (event) => { ACTION_HANDLER_MAP[event.target.getAttribute("shopby-action")]?.(); }); // 동의 사항 보기 버튼 document.querySelector("button[shopby-action=OPEN_APPLICATION_NOTICE]").addEventListener("click", (event) => { ACTION_HANDLER_MAP[event.target.getAttribute("shopby-action")]?.(); }); document.querySelector("button[shopby-action=OPEN_CANCEL_POLICY]").addEventListener("click", (event) => { ACTION_HANDLER_MAP[event.target.getAttribute("shopby-action")]?.(); }); EventManager.on("PAGE_LOAD_COMPLETED", async () => { const formEl = document.querySelector("#application-form"); const useUserInfo = document.querySelector("#useUserInfo"); if(!useUserInfo) return; const { data: profile } = await actions.queryProfile(); if(!profile) return; if(!profile.principalCertificated) { EventManager.fire("MODAL_ALERT_OPEN", { message: "본인 인증 후 구매 가능합니다.", onClose: () => { location.href = '/pages/sign-in/member-authentication.html'; }, }); return; } // 회원 정보와 동일 useUserInfo.addEventListener("click", (event) => { const checked = event.target.checked; const userInfoFormElements = ["name", "gender", "birthday", "phone"]; if(checked) { if(!profile.birthday || !profile.sex || profile.sex === "X" || !profile.mobileNo) { event.preventDefault(); EventManager.fire("MODAL_CONFIRM_OPEN", { message: "회원 정보에 필수 정보(생년월일, 성별, 휴대폰 번호 등)가 없습니다.
회원 정보 수정 후 이용하시거나, 직접 입력 바랍니다.", onConfirm: () => { window.location.href = '/pages/my/modify-member.html'; }, cancelLabel: "직접 입력", confirmLabel: "회원 정보 수정", }); return; } fillFormWithUserInfo(formEl, profile); } userInfoFormElements.forEach((name) => { const field = formEl.elements[name]; field.disabled = checked; if(field.name === "gender") { field.parentElement.querySelectorAll("input[type=checkbox]").forEach((el) => { el.disabled = checked; }); } }); }); // 폼 세팅 setFormRules(formEl); }); // 메시지 수신 이벤트 window.addEventListener("message", (event) => { const formEl = document.querySelector("#application-form"); if (!event.data || event.data.action !== "SELECT_SCHOOL" || !formEl) return; const { schoolName } = event.data.data; formEl.academyName.value = schoolName; formEl.academyName.removeAttribute("data-invalid"); }); })(); class CustomProductSummary extends ShopbySkin.modules.ProductSummary { setup() { const originalHandleReceiveProductDetail = this.handleReceiveProductDetail.bind(this); this.handleReceiveProductDetail = (product) => { const isRehearsalProduct = !(!product.brand || product.brand?.brandNo !== DSDOENV.RehearsalConfig.REHEARSAL_BRAND_NO); originalHandleReceiveProductDetail(product); } super.setup(); } } customElements.define("custom-product-summary", CustomProductSummary); class CustomPurchase extends ShopbySkin.modules.Purchase { isRehearsalProduct = true; setup() { const originalHandleReceiveProductDetail = this.handleReceiveProductDetail.bind(this); this.handleReceiveProductDetail = (product) => { if(!product.brand || product.brand?.brandNo !== DSDOENV.RehearsalConfig.REHEARSAL_BRAND_NO) { this.isRehearsalProduct = false; this.store.setState({ flatOptions: [], selectorOptions: [], unpurchasable: true }) this.eventManager.fire("MODAL_ALERT_OPEN", { message: "유효하지 않은 상품입니다.", onClose: () => { location.href = '/'; }, }); return; } this.isRehearsalProduct = true; originalHandleReceiveProductDetail(product); } const originalHandleReceiveFlatOptions = this.handleReceiveFlatOptions.bind(this); this.handleReceiveFlatOptions = (flatOptions) => { originalHandleReceiveFlatOptions(this.isRehearsalProduct ? flatOptions : []); } const originalHandleReceiveSelectorOptions = this.handleReceiveSelectorOptions.bind(this); this.handleReceiveSelectorOptions = (selectorOptions) => { originalHandleReceiveSelectorOptions(this.isRehearsalProduct ? selectorOptions : []); } super.setup(); } } customElements.define("custom-product-purchase", CustomPurchase);