(() => {
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: "상품이 관심상품 리스트에서
제거되었습니다.