(() => { const { pageHelper, EventManager, utils: { changeProfileFormVerifyMap, getLocalStorage, setLocalStorage, removeLocalStorage, handleFocusEvent, isSignedIn, }, } = ShopbySkin; const signUpFormHelper = pageHelper.signUpFormHelper(); signUpFormHelper.initialize({ helperKey: "sign-up-form", }); //helper와 localstorage 상태 일치 if (window.location.pathname.includes('/terms.html')) { removeLocalStorage("STORED-TERMS"); } // 메시지 수신 이벤트 window.addEventListener("message", (event) => { if (!event.data || event.data.action !== "SELECT_SCHOOL") return; const {schoolName, schoolCode} = event.data.data; // 입력 필드에 값 바인딩 const schoolInputField = document.querySelector( '.school-input-box input[placeholder="ex) -고등학교, -학원 "]' ); const schoolCodeField = document.querySelector( ".school-input-box-code-field input" ); if (schoolInputField) { schoolInputField.value = schoolName; } if (schoolCodeField) { schoolCodeField.value = schoolCode; } // 헬퍼 상태 업데이트 const state = signUpFormHelper.getState(); // 현재 상태 가져오기 const {profileExtraInformation} = state; if (profileExtraInformation?.extraInfoContents) { profileExtraInformation.extraInfoContents.forEach((info) => { if (info.extraInfoName === "소속학교(학원)") { info.extraInfoOptionTextContent = schoolName; // 소속학교 값 저장 } else if (info.extraInfoName === "소속코드") { info.extraInfoOptionTextContent = schoolCode; // 소속코드 값 저장 } }); } }); if (isSignedIn()) { if (!document.referrer || location.href === document.referrer) { history.back(); return; } location.href = document.referrer; } const containerEl = document.querySelector( `[shopby-helper-key="sign-up-form"]` ); const flattenRequest = (helper) => { const { profileBasicInformation, profileNicknameInformation, profileEmailInformation, profileSmsInformation, profileCertification, profileOptionalInformation, termsInformation, profileExtraInformation, } = helper.getState(); return { ...profileBasicInformation, ...profileEmailInformation, ...profileSmsInformation, ...profileCertification, ...profileOptionalInformation, extraInfo : profileExtraInformation?.extraInfoContents, nickname : profileNicknameInformation?.nickname, joinTermsAgreements: termsInformation?.terms, customTermsNos : termsInformation?.customTerms, }; }; // 비밀번호만 샵바이 기본 규칙과 다르게 적용 (영문/숫자/특수문자 모두 조합 8~20자), D.up과 통일 const PASSWORD_COMPOSITION_MESSAGE = "비밀번호는 영문, 숫자, 특수문자를 포함한 8~20자리로 입력해 주세요."; changeProfileFormVerifyMap.password = ({ value = "" }) => { const isLengthValid = value.length >= 8 && value.length <= 20; const isCompositionValid = /[a-zA-Z]/.test(value) && /[0-9]/.test(value) && /[^a-zA-Z0-9]/.test(value); const isValid = isLengthValid && isCompositionValid; return { isValid, message: isValid ? "" : PASSWORD_COMPOSITION_MESSAGE, }; }; const checkInvalidProfileForm = (request) => { // eslint-disable-next-line complexity const errors = Object.keys(changeProfileFormVerifyMap)?.map((key) => { const value = request?.[key]?.value ?? ""; switch (key) { case "memberId": return { ...changeProfileFormVerifyMap?.[key]({ value, isDuplicated : request?.memberId?.isDuplicate, isDormantMemberId: !!request?.isRecentlyExitedMember, }), field: key, }; case "passwordConfirm": return { ...changeProfileFormVerifyMap?.[key]({ value, comparisonValue: request?.password?.value, }), field: key, }; case "memberName": return { ...changeProfileFormVerifyMap?.[key]({ value, isRequired: request?.memberName?.isRequired, }), field: key, }; case "nickname": return { ...changeProfileFormVerifyMap?.[key]({ value, isDuplicated: request?.nickname?.isDuplicate, isRequired : request?.nickname?.isRequired, }), field: key, }; case "email": return { ...changeProfileFormVerifyMap?.[key]({ value, isDuplicated: request?.email?.isDuplicate, isRequired : request?.email?.isRequired, }), field: key, }; case "mobileNo": case "telephoneNo": return { ...changeProfileFormVerifyMap?.[key]({ value, isRequired: request?.[key]?.isRequired, }), field: key, }; case "detailAddress": return { ...changeProfileFormVerifyMap?.[key]({ value, zipCode : request?.zipCd, isRequired: request?.detailAddress?.isRequired, }), field: key, }; case "birthday": case "sex": return { ...changeProfileFormVerifyMap?.[key]({ value, isRequired: request?.[key]?.isRequired, }), field: key, }; case "extraInfo": return { ...changeProfileFormVerifyMap?.[key](request.extraInfo), field: key, }; case "joinTermsAgreements": return { ...changeProfileFormVerifyMap?.[key](request.joinTermsAgreements), field: key, }; default: return { ...changeProfileFormVerifyMap?.[key]({ value, isRequired: request?.[key]?.isRequired, }), field: key, }; } }); return errors.filter((error) => !error.isValid); }; // eslint-disable-next-line complexity const checkCertificatedValidation = (request) => { const invalidEmail = request?.emailCertificationStatus === "INITIAL"; const invalidSmsInternalCertification = request?.smsCertificationStatus === "INITIAL"; const invalidSmsExternalAuthentication = request?.smsCertificationStatus === "SMS_AUTHENTICATION" && !request?.ci; if ( invalidEmail || invalidSmsInternalCertification || invalidSmsExternalAuthentication ) { EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message : `${ invalidEmail ? "이메일 인증을 진행해 주세요." : "이미 사용중인 휴대폰 번호입니다." }`, onClose : () => { handleFocusEvent({ containerEl, fields: invalidEmail ? "email" : "mobileNo", }); }, }); return false; } const message = request.certificatedNumber?.length ? "인증을 진행해주세요." : "인증번호를 입력해주세요."; if ( request.certificated && (!request.certificated.value || !request.certificated.isValid) ) { EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message : `${request.certificated.message ?? message}`, onClose : () => { handleFocusEvent({containerEl, fields: "certificatedNumber"}); }, }); return false; } return true; }; const CLICK_EVENT_HANDLER_MAP = { // eslint-disable-next-line complexity EMAIL_CERTIFICATION: async (helper) => { const {profileEmailInformation} = helper.getState(); const isInvalidEmail = profileEmailInformation?.email.value === "@" || !profileEmailInformation?.email.isValid; if (isInvalidEmail) { EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message : `${ profileEmailInformation?.email.message ?? "이메일을 입력해주세요." }`, }); return; } if (profileEmailInformation?.emailCertificationStatus === "INITIAL") { await helper.sendCertificationCode( profileEmailInformation.email.value, "EMAIL" ); EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "SUCCESS", message : "인증번호가 발송되었습니다.", }); } else { EventManager.fire("MODAL_CONFIRM_OPEN", { noticeType: "WARNING", message : "인증번호를 재발송하시겠습니까?", onConfirm : async () => { await helper.sendCertificationCode( profileEmailInformation.email.value, "EMAIL" ); EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "SUCCESS", message : "인증번호가 발송되었습니다.", }); }, }); } }, // eslint-disable-next-line complexity SMS_CERTIFICATION : async (helper) => { const {profileSmsInformation} = helper.getState(); const isInvalidMobileNo = !profileSmsInformation?.mobileNo.value || !profileSmsInformation?.mobileNo.isValid; if (isInvalidMobileNo) { EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message : `${ profileSmsInformation?.mobileNo.message ?? "휴대폰 번호를 입력해주세요." }`, }); return; } if (profileSmsInformation?.smsCertificationStatus === "INITIAL") { await helper.sendCertificationCode( profileSmsInformation.mobileNo.value, "SMS" ); EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "SUCCESS", message : "인증번호가 발송되었습니다.", }); } else { EventManager.fire("MODAL_CONFIRM_OPEN", { noticeType: "WARNING", message : "인증번호를 재발송하시겠습니까?", onConfirm : async () => { await helper.sendCertificationCode( profileSmsInformation.mobileNo.value, "SMS" ); EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "SUCCESS", message : "인증번호가 발송되었습니다.", }); }, }); } }, AUTHENTICATION_BY_PHONE: () => { EventManager.fire("OPEN_LAYER_MODAL", { name : "kcp-sms-authentication", data : {type: "JOIN_TIME"}, onClose: ({reason, state}) => { if (reason === "DID_SUBMIT") { ShopbySkin.EventManager.fire("SUCCESS_AUTHENTICATION_SMS", state); } }, }); }, SEARCH_ZIP_CODE : () => { EventManager.fire("OPEN_LAYER_MODAL", { isFull : false, modalAddClass: "search-zip-code", name : "page-zip-code", onClose : ({reason, state}) => { if (reason === "DID_SUBMIT") { ShopbySkin.EventManager.fire("SELECT_ZIP_CODE", { moduleKey: "profile-optional-information", state, }); } }, }); }, SHOW_TERM_DETAIL : ({elTarget, helper}) => { const {termsInformation} = helper.getState(); if (!termsInformation) { return; } const mergedTerms = [ ...termsInformation.terms, ...termsInformation.customTerms, ]; const selectedTerm = mergedTerms.find( (term) => elTarget.getAttribute("shopby-term-id") === term.id.toString() ); EventManager.fire("OPEN_LAYER_MODAL", { name: "term-detail", data: selectedTerm, }); }, SEARH_CLASS_MODAL : () => { EventManager.fire("OPEN_LAYER_MODAL", { isFull : false, name : "search-class-modal", onClose: ({reason, state}) => { console.log("TEST", reason, state); }, }); }, // eslint-disable-next-line complexity SUBMIT: async (helper) => { const flattedRequest = flattenRequest(helper); const localStorageSaved = JSON.parse( getLocalStorage("STORED-TERMS") ?? "[]" // 가져올 때, PC_CHECK_TERMS 를 안 붙임! 주의 ); // flattedRequest.joinTermsAgreements 배열 에 업데이트 flattedRequest.joinTermsAgreements.forEach((joinTermsAgreementTerm) => { localStorageSaved.forEach((savedValue) => { if (joinTermsAgreementTerm.id === savedValue.id) { joinTermsAgreementTerm.checked = savedValue.checked; } }); }); //localstorage에 저장된 데이터 값으로 helper customTermsNo 동기화 flattedRequest.customTermsNos = flattedRequest.customTermsNos .map(term => { const saved = localStorageSaved.find(savedValue => savedValue.id === term.id); return { ...term, checked: saved ? saved.checked : term.checked }; }) .filter(term => term.checked) .map(term => Number(term.id)); const smsAgreed = localStorageSaved.find(term => term.id === 'smsAgreed'); const directMailAgreed = localStorageSaved.find(term => term.id === 'directMailAgreed'); flattedRequest.smsAgreed.value = smsAgreed?.checked || false; flattedRequest.directMailAgreed.value = directMailAgreed?.checked || false; const invalidRequest = checkInvalidProfileForm(flattedRequest); if (invalidRequest?.length) { const [error] = invalidRequest; EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message: `${error.message}`, onClose: () => { handleFocusEvent({ containerEl, fields: error.field }); EventManager.fire("INVALID_PROFILE_FORM", { data: invalidRequest, }); }, }); return; } if (!checkCertificatedValidation(flattedRequest)) { return; } const { data } = await helper.submit(flattedRequest); if (data.joinYmdt ?? false) { removeLocalStorage("STORED-TERMS"); // 회원가입 완료시, 회원가입 시점으로 분기처리하여, 로컬 스토리지 terms 지우기 } EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "SUCCESS", message: "회원가입이 완료되었습니다.", onClose: () => { if (data.memberStatus === "PENDING") { location.replace("/pages/sign-up/sign-up-pending.html"); } else { location.replace("/pages/sign-up/sign-up-complete.html"); } }, }); }, CANCEL: () => { removeLocalStorage("STORED-TERMS"); // 취소시, 로컬 스토리지 terms 지우기 }, TERMS_SUBMIT: async (helper) => { const state = helper.getState(); const {termsInformation, profileSmsInformation, profileEmailInformation} = state; const {terms, customTerms} = termsInformation; const requiredTermsChecked = terms .filter(term => term.required) .every(term => term.checked); const localStorageSaved = JSON.parse(getLocalStorage( "STORED-TERMS") || "[]"); const [ Term157 ] = customTerms; const smsAgreed = localStorageSaved.find(term => term.id === 'smsAgreed')?.checked; const directMailAgreed = localStorageSaved.find(term => term.id === 'directMailAgreed')?.checked; if (!requiredTermsChecked) { return EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message : "필수 약관에 모두 동의해주세요.", }); } const subTerms = !smsAgreed && !directMailAgreed; if (Term157.checked && subTerms) { return EventManager.fire("MODAL_ALERT_OPEN", { noticeType: "CAUTION", message : "수신 방법을 최소 1개 이상 선택해 주세요.", }); } if (!Term157.checked) { return EventManager.fire("MODAL_CONFIRM_OPEN", { message : "

광고성 정보 수신에 동의하지 않으시면,

\

이벤트 및 혜택 관련 안내 대상에서 제외될 수 있습니다.


\

계속 진행하시겠습니까?

", cancelLabel : "종료", confirmLabel: "계속 진행", onConfirm : () => { location.href = "/pages/sign-up/sign-up-form.html"; }, }); } location.replace("/pages/sign-up/sign-up-form.html"); }, }; const ALL_TERM_IDS = [ "on", // 전체 동의 "pi_14_age", // 만 14세 이상 동의 "use", // 이용 약관 "pi_collection_and_use_required", // 개인 정보 수집/이용 "157", // 광고성 정보 수신 동의 "158", // 마케팅 목적의 개인정보 수집 및 이용 "onSubTerms", // 광고성 정보 수신 방법 전체 동의 "directMailAgreed", // email "smsAgreed" // sms ]; const getTerms = () => JSON.parse(getLocalStorage( "STORED-TERMS") || "[]"); const setTerms = (terms) => setLocalStorage( "STORED-TERMS", JSON.stringify(terms)); const getCheckboxes = () => ({ sms: containerEl.querySelector('input[type="checkbox"][value="smsAgreed"]'), email: containerEl.querySelector('input[type="checkbox"][value="directMailAgreed"]'), on: containerEl.querySelector('input[type="checkbox"][value="on"]'), onSubTerms: containerEl.querySelector('input[type="checkbox"][value="onSubTerms"]') }); const updateDisabledBy157Term = (terms) => { const { sms, email, onSubTerms } = getCheckboxes(); const term157 = terms.find(term => term.id === '157'); const shouldDisable = !term157?.checked; sms.disabled = shouldDisable; email.disabled = shouldDisable; onSubTerms.disabled = shouldDisable; } const updateCheckboxStateByTerms = (terms) => { const { sms, email, on, onSubTerms } = getCheckboxes(); const checkboxMap = { smsAgreed: sms, directMailAgreed: email, onSubTerms: onSubTerms, on : on }; for (const term of terms) { if (checkboxMap[term.id]) { checkboxMap[term.id].checked = term.checked; } } }; //약관 체크 상태 업데이트 const updateTermChecked = (terms, termId, checked) => { const exists = terms.some(t => t.id === termId); return exists ? terms.map(term => (term.id === termId ? { ...term, checked } : term)) : [...terms, { id: termId, checked }]; }; const checkAllTermsSelected = (terms) => { const excludeIds = ["on"]; return ALL_TERM_IDS .filter(id => !excludeIds.includes(id)) .every(id => terms.find(t => t.id === id)?.checked); }; const checksubTermsSubTermsSelected = () => { const { sms, email } = getCheckboxes(); return sms.checked && email.checked; } const toggleAllSubTerms = (checked) => { // 이메일·SMS 수신 전체 동의는 Shopby에서 제공하는 전체 선택 함수(예: CHECK_ALL_TERMS)가 없어 // UI 상태가 자동으로 동기화 되지 않는 상태. 해당 코드에서 UI 업데이트를 직접 처리 중. let terms = getTerms(); ["smsAgreed", "directMailAgreed", "onSubTerms"].forEach(id => { terms = updateTermChecked(terms, id, checked); }); const allChecked = checkAllTermsSelected(terms); terms = updateTermChecked(terms, "on", allChecked); setTerms(terms); updateCheckboxStateByTerms(terms); } //전체 약관 토글 const toggleAllTerms = (checked) => { const updatedTerms = ALL_TERM_IDS.map(id => { return { id, checked: checked }; }); setTerms(updatedTerms); if (checked) { updateDisabledBy157Term(updatedTerms); updateCheckboxStateByTerms(updatedTerms); } else { updateDisabledBy157Term(updatedTerms); } }; const toggleTerm = (termId, checked) => { let terms = getTerms(); terms = updateTermChecked(terms, termId, checked); const allChecked = checkAllTermsSelected(terms); terms = updateTermChecked(terms, "on", allChecked); if(termId === '157' && !checked){ ["smsAgreed", "directMailAgreed", "onSubTerms"].forEach(id => { terms = updateTermChecked(terms, id, checked); }); } setTerms(terms); updateDisabledBy157Term(terms); // 157 상태에 따라 subTerm disabled updateCheckboxStateByTerms(terms); }; const toggleSubTerm = (termId, checked) => { let terms = getTerms(); terms = updateTermChecked(terms, termId, checked); const allCheckedSubTerms = checksubTermsSubTermsSelected(); const { onSubTerms } = getCheckboxes(); onSubTerms.checked = allCheckedSubTerms; terms = updateTermChecked(terms, "onSubTerms", allCheckedSubTerms); const allChecked = checkAllTermsSelected(terms); terms = updateTermChecked(terms, "on", allChecked); setTerms(terms); updateCheckboxStateByTerms(terms); } const clickEventListener = ({ target }) => { const action = target.getAttribute("shopby-action"); const { type, value: termId, checked } = target; if (type === "checkbox") { if (termId === "on") { return toggleAllTerms(checked); } if (termId === "onSubTerms") { return toggleAllSubTerms(checked); } const SUB_TERMS = ["smsAgreed", "directMailAgreed"]; if (SUB_TERMS.includes(termId)) { return toggleSubTerm(termId, checked); } return toggleTerm(termId, checked); } const handler = CLICK_EVENT_HANDLER_MAP[action]; if (!handler) return; if (action === "SHOW_TERM_DETAIL") { handler({ helper: signUpFormHelper, elTarget: target }); } else { handler(signUpFormHelper); } }; containerEl.addEventListener("click", clickEventListener); })();