(() => { const { pageHelper, EventManager, utils, actions, constants } = ShopbySkin; const { unescapeHTML } = utils; const productOptionAction = actions.getProductOptionAction(); const productOptionEl = document.querySelector('product-purchase-v2'); const orderSheetAppCardEl = document.querySelector('order-sheet-app-card'); EventManager.on('QUERY_PRODUCT_DETAIL', async (product) => { await productOptionAction.fetchOptions(product.productNo, { productInfo: product, }); productOptionAction.fetchOptionSelector(); }); EventManager.on('PRODUCT_DETAIL_CONTENT_LOADED', () => { const imgs = document.querySelector('product-detail').querySelectorAll('img'); utils.lazyLoad({ imgEls: imgs }); }); EventManager.on('PRODUCT_DETAIL_OPTION_IMAGES_LOADED', () => { const imgs = document.querySelector('[slot="optionImages"]')?.querySelectorAll('img'); if (!imgs) { return; } utils.lazyLoad({ imgEls: imgs }); }); EventManager.on('CHECK_PRODUCT_CONTENT_SIZE', () => { // 상품 펼쳐보기 버튼 생성, hidden 처리 const getProductDetailContentElement = () => document.querySelector('[shopby-element="product-content-detail"]'); const observer = new ResizeObserver(() => { const contentEl = getProductDetailContentElement(); const images = contentEl.querySelectorAll('img'); const imageLoadProcess = []; if (images.length > 0) { images.forEach((image) => { const result = new Promise((resolve) => { image.onload = resolve; }); imageLoadProcess.push(result); }); } Promise.allSettled(imageLoadProcess).then(() => { if (contentEl.offsetHeight > 500) { const target = document.querySelector('[shopby-element="product-content"]'); target.classList.add('hidden'); const moreBtnTarget = document.querySelector('[shopby-element="TOGGLE_SHOW_MORE"]'); moreBtnTarget.classList.remove('hidden'); } }); }); const observerTarget = getProductDetailContentElement(); observerTarget && observer.observe(observerTarget); // 상품 펼쳐보기 버튼 생성, hidden 처리 END }); const searchParams = new URLSearchParams(location.search); const productNo = searchParams.get('productNo'); const channelType = searchParams.get('channelType'); const trackingKey = searchParams.get('trackingKey'); const cartAction = actions.getCartAction(); utils.setChannelType(channelType); const { productDetailHelper } = pageHelper; const { initialize, deleteProductInquiryBy, searchReviewsV2 } = productDetailHelper(); initialize({ productNo, channelType, cartAction, productOptionAction, disableInitFetchProductOptions: true, }); EventManager.on('QUERY_PRODUCT_DETAIL', (data) => { EventManager.fire('CHANGE_SUB_TITLE', { subTitle: data?.baseInfo?.productName, }); // 외부스크립트 등록 기능. 삭제금지 ShopbyExternalScript?.setGlobalObjectSb({ product: { ...data } }); }); const redirectLoginPage = () => { const nextUrl = `${location.origin}${constants.PAGE.SIGN_IN}`; utils.moveTo({ url: `${nextUrl}?from=${encodeURIComponent(location.href)}`, }); }; const getController = (target) => { const controllerId = target.getAttribute('shopby-controller-id') || target.closest('[shopby-controller-id]').getAttribute('shopby-controller-id'); if (!controllerId) { return null; } const { data } = productOptionAction.fetchOptionSelector(controllerId); return data; }; const validationMap = { receiverName: (value) => { if (!value) { return { message: '받는 사람을 입력해주세요', slotName: 'shippingInfo.receiverName', }; } return false; }, receiverAddress: (value) => { if (!value) { return { message: '주소를 입력해주세요.', slotName: 'shippingInfo.receiverAddress', }; } return false; }, receiverDetailAddress: (value) => { if (!value) { return { message: '상세 주소를 입력해주세요.', slotName: 'shippingInfo.receiverDetailAddress', }; } return false; }, receiverContact1: (value) => { const MIN_CONTACT_LENGTH = 10; const slotName = 'shippingInfo.receiverContact1'; if (!value) { return { message: '받는 사람 휴대폰 번호를 입력해주세요.', slotName, }; } if (value.length < MIN_CONTACT_LENGTH) { return { message: '휴대폰번호는 10자~12자 이내로 입력해주세요.', slotName, }; } return false; }, }; const checkValidation = ({ data, fields }) => { let errorInfo = null; for (let i = 0; i < fields.length; i += 1) { const fieldName = fields[i]; if (!validationMap[fieldName]) { continue; } const error = validationMap[fieldName](data[fieldName].trim(), data); if (error) { errorInfo = error; break; } } if (errorInfo?.message) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'CAUTION', message: `${errorInfo.message}`, }); return false; } return true; }; let shopbyCommonData = {}; EventManager.on('PAGE_LOAD_COMPLETED', (commonData) => { shopbyCommonData = commonData; // 외부스크립트 등록 기능. 삭제금지 ShopbyExternalScript?.setPageScriptType('PRODUCT'); }); const ACTION_HANDLER_MAP = { DISCOUNT_DETAIL: () => { EventManager.fire('OPEN_LAYER_MODAL', { title: '할인내역', name: 'discount-guide', data: { DISCOUNT_LABEL_MAP: { additionDiscountAmt: '추가할인', immediateDiscountAmt: '즉시할인', }, }, modalAddClass: 'no-full', classModifier: 'discount-price-information', onClose: () => null, }); }, COUPON_DOWNLOAD: () => { if (!utils.isSignIn()) { ShopbySkin.EventManager.fire('MODAL_ALERT_OPEN', { message: '로그인 후 이용할 수 있습니다.', onClose: redirectLoginPage, }); return; } ShopbySkin.EventManager.fire('OPEN_LAYER_MODAL', { title: '쿠폰받기', name: 'coupon-download', data: { productNo, channelType, }, isFull: true, onClose: () => null, }); }, PRODUCT_INQUIRY_WRITE: (target) => { const guestWriteable = target.getAttribute('shopby-guest-writeable') === 'true'; if (!utils.isSignIn() && !guestWriteable) { ShopbySkin.EventManager.fire('MODAL_ALERT_OPEN', { message: '로그인 후 이용할 수 있습니다.', onClose: redirectLoginPage, }); return; } const productName = target.closest('[shopby-product-name]')?.getAttribute('shopby-product-name'); const imageUrl = target.closest('[shopby-image-url]')?.getAttribute('shopby-image-url'); const imageType = target.closest('[shopby-image-type]')?.getAttribute('shopby-image-type'); const { inquiryTags } = document.querySelector('product-detail-inquiry-list').store.state; EventManager.fire('OPEN_LAYER_MODAL', { name: 'product-inquiry-form', title: '상품 문의', data: { productNo, productName, unescapedProductName: unescapeHTML(productName), imageUrl, imageType, isModify: false, commonData: shopbyCommonData, useSelectProduct: false, inquiryTags, }, onClose: ({ reason }) => { if (reason === 'DID_SUBMIT') { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'success', message: '상품문의가 등록됐습니다.', onClose: () => { location.reload(); }, }); } }, }); }, INQUIRY_DETAIL: (target) => { const readable = target.closest('[shopby-readable]')?.getAttribute('shopby-readable') === 'true'; if (!readable) { EventManager.fire('MODAL_ALERT_OPEN', { message: '비밀글 조회 권한이 없습니다.', }); } }, INQUIRY_DELETE: (target) => { const inquiryNo = target.getAttribute('shopby-inquiry-no'); EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'warning', message: '
삭제 시 복구가 불가능합니다.
정말 삭제하시겠습니까?
삭제 시 복구가 불가하며,
지급된 적립금이 차감됩니다.
정말 삭제하시겠습니까?
삭제 시 복구가 불가능합니다.
정말 삭제하시겠습니까?
상품이 장바구니에 담겼습니다. 바로 확인하시겠습니까?
', cancelLabel: '쇼핑계속하기', confirmLabel: '장바구니 가기', onConfirm: () => { utils.moveTo({ url: constants.PAGE.CART, }); }, }); }, PRODUCT_ORDER: async () => { const { isSignedIn } = utils; const { data } = productOptionAction.fetchUserOptionSelections(); const { requestOrderSheet } = pageHelper.productDetailHelper(); const { orderSheetNo } = await requestOrderSheet({ cartNos: null, channelType, productCoupons: null, trackingKey, products: data.map((item) => ({ ...item, productNo, orderCnt: item.count, additionalProductNo: 0, channelType, })), }); const orderSheetNoURL = `${location.origin}${constants.PAGE.ORDER_SHEET_FORM}?ordersheetNo=${orderSheetNo}`; const nextUrl = isSignedIn() ? orderSheetNoURL : `${location.origin}${constants.PAGE.SIGN_IN}?from=${orderSheetNoURL}`; utils.moveTo({ url: nextUrl }); }, PAY_APP_CARD: async () => { // 앱카드 결제 if (!utils.isSignIn()) { EventManager.fire('MODAL_ALERT_OPEN', { message: '로그인하셔야 본 서비스를 이용하실 수 있습니다', onClose: redirectLoginPage, }); return; } const { data } = productOptionAction.fetchUserOptionSelections(); const { requestOrderSheet } = pageHelper.productDetailHelper(); const { orderSheetNo } = await requestOrderSheet({ cartNos: null, channelType, productCoupons: null, trackingKey, products: data.map((item) => ({ ...item, productNo, orderCnt: item.count, additionalProductNo: 0, channelType, })), }); const orderSheetFormHelper = pageHelper.orderSheetFormHelper(); orderSheetFormHelper.orderSheetNo = orderSheetNo; await orderSheetFormHelper.actions.fetchOrderSheet({ orderSheetNo, includesMemberAddress: false, }); await orderSheetFormHelper.actions.fetchMyShippingAddress(); await orderSheetFormHelper.fetchPaymentInfo(); const amount = data?.reduce((acc, cur) => acc + cur.calcPrice, 0); await orderSheetFormHelper.actions.fetchAppCardCards({ amount, }); }, PRODUCT_LIKE: (target) => { const productNo = target.closest('[shopby-product-no]').getAttribute('shopby-product-no'); if (!productNo) { return; } ShopbySkin.actions.toggleLikeStatus({ productNo, onSuccess: (isLiked) => { const el = target.closest('[type=button]'); if (isLiked) { el.classList.add('is-active'); return; } el.classList.remove('is-active'); }, }); }, TOGGLE_SHOW_MORE: (target) => { const toggleTarget = document.querySelector('[shopby-element="product-content"]'); const status = toggleTarget.classList.toggle('hidden'); target.innerText = status ? '상세정보 펼쳐보기' : '상세정보 접기'; }, WRITE_REVIEW_REPORT: (target) => { const reviewNo = target.closest('[shopby-review-no]')?.getAttribute('shopby-review-no'); if (!utils.isSignedIn()) { EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '로그인 후 이용할 수 있습니다.', onConfirm: () => { utils.moveTo({ url: `/pages/sign-in/sign-in.html?from=${encodeURIComponent(location.href)}` }); }, }); return; } EventManager.fire('OPEN_LAYER_MODAL', { name: 'report-form', title: '신고하기', isFull: false, onClose: ({ reason }) => { if (reason === 'DID_SUBMIT') { location.reload(); } }, onConfirm: async ({ reportReasonType, content }) => { const { status } = await actions.postProductReviewReport({ productNo, reviewNo, reportReasonCd: reportReasonType, content, }); if (status === 204) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'success', message: '신고 되었습니다.', onClose: () => { location.reload(); }, }); } }, }); }, CANCEL_REVIEW_REPORT: (target) => { const reviewNo = target.closest('[shopby-review-no]')?.getAttribute('shopby-review-no'); if (!utils.isSignedIn()) { EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '로그인 후 이용할 수 있습니다.', onConfirm: () => { utils.moveTo({ url: `/pages/sign-in/sign-in.html?from=${encodeURIComponent(location.href)}` }); }, }); return; } EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '신고를 취소 하시겠습니까?', confirmLabel: '확인', onConfirm: async () => { const { status } = await actions.deleteProductReviewReport({ productNo, reviewNo, }); if (status === 204) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'success', message: '신고가 취소 되었습니다.', onClose: () => { location.reload(); }, }); } }, }); }, // 상품문의 신고 WRITE_INQUIRY_REPORT: (target) => { const inquiryNo = target.closest('[shopby-inquiry-no]')?.getAttribute('shopby-inquiry-no'); if (!utils.isSignedIn()) { EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '로그인 후 이용할 수 있습니다.', onConfirm: () => { utils.moveTo({ url: `/pages/sign-in/sign-in.html?from=${encodeURIComponent(location.href)}` }); }, }); return; } EventManager.fire('OPEN_LAYER_MODAL', { name: 'report-form', title: '신고하기', isFull: false, onClose: ({ reason }) => { if (reason === 'DID_SUBMIT') { location.reload(); } }, onConfirm: async ({ reportReasonType, content }) => { const { status } = await actions.postProductInquiryReport({ productNo, inquiryNo, reportType: reportReasonType, reason: content, }); if (status === 204) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'success', message: '신고 되었습니다.', onClose: () => { location.reload(); }, }); } }, }); }, // 상품 문의 신고 취소 CANCEL_INQUIRY_REPORT: (target) => { const inquiryNo = target.closest('[shopby-inquiry-no]')?.getAttribute('shopby-inquiry-no'); if (!utils.isSignedIn()) { EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '로그인 후 이용할 수 있습니다.', onConfirm: () => { utils.moveTo({ url: `/pages/sign-in/sign-in.html?from=${encodeURIComponent(location.href)}` }); }, }); return; } EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '신고를 취소 하시겠습니까?', confirmLabel: '확인', onConfirm: async () => { const { status } = await actions.deleteProductInquiryReport({ productNo, inquiryNo, }); if (status === 204) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'success', message: '신고가 취소 되었습니다.', onClose: () => { location.reload(); }, }); } }, }); }, }; const ACTION_CHANGE_HANDLER_MAP = { SORT_REVIEWS: (target) => { const { value } = target; EventManager.fire('CHANGE_SORT_REVIEWS', { value }); }, }; const CLICK_ACTION_HANDLER_MAP = { HANDLE_COUNT_BTN_CLICK: (target) => { const controller = getController(target); if (!controller) { return; } const controllerEl = target.closest('[shopby-controller-id]'); const optionId = controllerEl.getAttribute('shopby-option-id'); const count = Number(controllerEl.getAttribute('shopby-count')); const incremental = target.getAttribute('shopby-variation') === 'INCREASE'; controller.changeCount({ optionId, count: incremental ? count + 1 : count - 1, }); productOptionAction.fetchOptionSelector(); }, REMOVE_OPTION: (target) => { const controller = getController(target); if (!controller) { return; } const controllerEl = target.closest('[shopby-controller-id]'); const optionId = controllerEl.getAttribute('shopby-option-id'); controller.removeOption({ optionId }); productOptionAction.fetchOptionSelector(); }, TOGGLE_TEXT_OPTIONS_BY_QUANTITY: (target) => { const controller = getController(target); if (!controller) { return; } const controllerEl = target.closest('[shopby-controller-id]'); const optionId = controllerEl.getAttribute('shopby-option-id'); const inputNo = Number(controllerEl.getAttribute('shopby-input-no')); const type = controllerEl.getAttribute('shopby-text-option-type'); const count = Number(controllerEl.getAttribute('shopby-count')); controller.toggleTextOptionType({ optionId, inputNo, type, count, }); productOptionAction.fetchOptionSelector(); }, // eslint-disable-next-line complexity CLICK_PAY_BTN: async () => { // 결제하기 const orderSheetFormHelper = pageHelper.orderSheetFormHelper(); const formData = orderSheetFormHelper.getFormData(); const { appCardInfo, shippingInfo, orderSheetTerms, hasDeliverableProduct, payMethods } = formData; const isValidShippingInfo = checkValidation({ data: shippingInfo, fields: ['receiverName', 'receiverAddress', 'receiverDetailAddress', 'receiverContact1'], }); if (hasDeliverableProduct && !isValidShippingInfo) { return; } const isNotValidCheckedTerms = orderSheetTerms.filter(({ required }) => required).some(({ checked }) => !checked); if (isNotValidCheckedTerms) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'CAUTION', message: '필수 약관을 확인해 주세요.', onClose: () => { const termCheckBox = document.querySelector('input[shopby-term-id]'); termCheckBox?.focus(); }, }); return; } const { pgType: selectedPgType } = payMethods.selectedPayMethod; if (selectedPgType === 'APP_CARD' && !appCardInfo.cardCode) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'CAUTION', message: '카드를 선택해 주세요.', }); return; } await orderSheetFormHelper.pay({ platform: ShopbySkin.platform, confirmUrl: `${location.origin}/pages/order/order-confirm.html?deliverable=${ hasDeliverableProduct ? 'Y' : 'N' }`, successCallback: (result) => { const { pgType, extraData } = result; const isOneClickPayAppCard = extraData?.authYn === 'Y'; if (pgType === 'APP_CARD' && !isOneClickPayAppCard) { EventManager.fire('OPEN_LAYER_MODAL', { name: 'app-card-authenticate', modalAddClass: 'app-card-authenticate-modal', isFull: true, data: { orderResponse: { ...result }, onRetry: CLICK_ACTION_HANDLER_MAP.CLICK_PAY_BTN, }, }); } }, }); }, SHOW_TERM_DETAIL: (target) => { const orderSheetFormHelper = pageHelper.orderSheetFormHelper(); const { orderSheetTerms } = orderSheetFormHelper.getFormData(); const termId = Number(target.getAttribute('shopby-term-id')); const foundTerm = orderSheetTerms.find(({ termsNo, customTermsNo }) => { const termNo = termsNo ?? customTermsNo; return termNo === termId; }); if (foundTerm) { EventManager.fire('OPEN_LAYER_MODAL', { name: 'term-detail', data: { ...foundTerm, title: foundTerm.termsName }, }); } }, CLICK_DELETE_CARD_ON_APP_CARD: (target) => { const acntId = target.closest('[shopby-acnt-id]')?.getAttribute('shopby-acnt-id'); if (!acntId) { return; } const orderSheetFormHelper = pageHelper.orderSheetFormHelper(); EventManager.fire('MODAL_CONFIRM_OPEN', { noticeType: 'WARNING', message: '카드를 삭제하시겠습니까?', confirmLabel: '확인', onConfirm: async () => { const { isSuccess } = await orderSheetFormHelper.actions.deleteAppCardPayCardsAcntId({ acntId }); if (isSuccess) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'SUCCESS', message: '카드가 삭제되었습니다.', }); const { data } = productOptionAction.fetchUserOptionSelections(); const amount = data?.reduce((acc, cur) => acc + cur.calcPrice, 0); orderSheetFormHelper.actions.fetchAppCardCards({ amount, }); } }, }); }, }; const CHANGE_ACTION_HANDLER_MAP = { SELECT_OPTION: (target) => { const controller = getController(target); if (!controller) { return; } const labelId = target.getAttribute('shopby-label-id'); const index = Number(target.getAttribute('shopby-index')); const optionId = target.value; controller.selectOption({ labelId, optionId, index, }); productOptionAction.fetchOptionSelector(); }, CHANGE_COUNT: (target) => { const controller = getController(target); if (!controller) { return; } const controllerEl = target.closest('[shopby-controller-id]'); const optionId = controllerEl.getAttribute('shopby-option-id'); const count = Number(target.value); controller.changeCount({ optionId, count, }); productOptionAction.fetchOptionSelector(); }, INPUT_TEXT_OPTION: (target) => { const controller = getController(target); if (!controller) { return; } const controllerEl = target.closest('[shopby-controller-id]'); const optionId = controllerEl.getAttribute('shopby-option-id'); const inputNo = Number(controllerEl.getAttribute('shopby-input-no')); const textOptionId = target.getAttribute('shopby-text-option-id'); controller.inputTextOption({ value: target.value, optionId, inputNo, textOptionId, }); productOptionAction.fetchOptionSelector(); }, }; const INPUT_ACTION_HANDLER_MAP = { INPUT_TEXT_OPTION: (target) => { const counterEl = target.closest('[shopby-counter]'); const countEl = counterEl.querySelector('[shopby-count]'); const errorMessageEl = counterEl.querySelector('[shopby-error-message]'); countEl.textContent = target.value.length; if (target.value.length > 100) { errorMessageEl.textContent = '텍스트옵션 입력글자수를 초과하였습니다.'; } else { errorMessageEl.textContent = ''; } }, }; // CLICK document.querySelector('product-summary')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(); }); document.querySelector('product-detail')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('product-detail-review-summary')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('product-detail-review-list')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('product-detail-board-button')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('[shopby-tab-element="PRODUCT_INQUIRY"]')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('product-purchase-v2')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('related-product')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); document.querySelector('product-detail-review-photo-collect')?.addEventListener('click', ({ target }) => { ACTION_HANDLER_MAP[target.closest('[shopby-action]')?.getAttribute('shopby-action')]?.(target); }); productOptionEl?.addEventListener('click', ({ target }) => { CLICK_ACTION_HANDLER_MAP[target.getAttribute('shopby-action')]?.(target); }); orderSheetAppCardEl?.addEventListener('click', ({ target }) => { CLICK_ACTION_HANDLER_MAP[target.getAttribute('shopby-action')]?.(target); }); // CHANGE document.querySelector('product-detail-review-list')?.addEventListener('change', ({ target }) => { ACTION_CHANGE_HANDLER_MAP[target.getAttribute('shopby-action')]?.(target); }); productOptionEl?.addEventListener('change', ({ target }) => { CHANGE_ACTION_HANDLER_MAP[target.getAttribute('shopby-action')]?.(target); }); productOptionEl?.addEventListener('input', ({ target }) => { INPUT_ACTION_HANDLER_MAP[target.getAttribute('shopby-action')]?.(target); }); })();