(() => {
const { EventManager, pageHelper } = ShopbySkin;
const { withdrawClaimByOrderOptionNo, confirmOrder, cancelOrder } = pageHelper.orderDetailHelper();
const { initialize } = pageHelper.myOrdersHelper();
const datePickerHelper = pageHelper.datePickerHelper();
datePickerHelper.initialize();
const searchParams = new URLSearchParams(location.search);
const queryParams = Object.fromEntries(searchParams.entries());
initialize(queryParams);
const openNaverPayAlertModal = (nextActionType, orderStatusType) => {
const ACTION_TYPE_LABEL = {
CANCEL: '취소신청',
EXCHANGE: '교환신청',
RETURN: '반품신청',
CONFIRM_ORDER: '구매확정',
};
if (
nextActionType === 'WRITE_REVIEW' &&
['DELIVERY_ING', 'DELIVERY_DONE', 'BUY_CONFIRM'].includes(orderStatusType)
) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message: '네이버페이 주문은 네이버페이에서 구매확정 이후 후기작성이 가능합니다.',
});
return;
}
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message: `네이버페이지 주문은 네이버페이에서 ${ACTION_TYPE_LABEL[nextActionType]} 하실 수 있습니다.`,
});
};
const openWithdrawClaimModal = (orderOptionNo, claimStatusLabel) => {
EventManager.fire('MODAL_CONFIRM_OPEN', {
noticeType: 'WARNING',
message: `${claimStatusLabel} 신청을 철회하시겠습니까?`,
onConfirm: async () => {
const { isSuccess } = await withdrawClaimByOrderOptionNo(orderOptionNo);
if (isSuccess) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'SUCCESS',
message: `${claimStatusLabel}신청 철회가 완료되었습니다.`,
onClose: () => location.reload(),
});
}
},
});
};
const validateWithdrawClaim = ({ claimNo, claimNos, claimStatusLabel, orderOptionNo }) => {
const hasAfterClaim = claimNos.split(',').some((no) => Number(no) > Number(claimNo));
if (!hasAfterClaim) {
return true;
}
EventManager.fire('MODAL_CONFIRM_OPEN', {
noticeType: 'WARNING',
message: '철회 시 후순위 클레임의 결제/환불 금액이 변동됩니다.
클레임을 철회하시겠습니까?',
onConfirm: () => openWithdrawClaimModal(orderOptionNo, claimStatusLabel),
});
return false;
};
// 데스크톱은 주문 화면을 유지한 채 배송조회를 새 창으로 연다.
const openDeliveryTracking = (deliveryUri) => {
if (!deliveryUri) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message: '배송 정보가 아직 등록되지 않았습니다.
잠시 후 다시 확인해주세요.',
});
return;
}
window.open(deliveryUri, '_blank');
};
// 배송중·배송완료 주문은 후기 작성이 곧 구매확정이라, 폼을 열기 전에 먼저 확인받는다.
// 확인하고 들어온 폼은 등록할 때 다시 묻지 않도록 confirmedOrderReceipt 를 넘긴다.
const confirmOrderReceipt = (orderStatusType, openForm) => {
if (!['DELIVERY_ING', 'DELIVERY_DONE'].includes(orderStatusType)) {
openForm();
return;
}
EventManager.fire('MODAL_CONFIRM_OPEN', {
noticeType: 'SUCCESS',
message: '상품을 받으셨나요?
후기 작성 시 구매확정 처리됩니다.',
onConfirm: openForm,
});
};
const MODULE_ACTION_HANDLER = {
SEARCH_ORDERS: () => {
const params = new URLSearchParams(location.search);
location.href = `${location.origin}${location.pathname}?${params.toString()}`;
},
CANCEL: ({ orderOptionNo, orderNo, orderOptionCount, payType, orderStatusType, orderNextActionType }) => {
const canCancelAll = orderNextActionType.split(',').includes('CANCEL_ALL');
if (payType === 'NAVER_PAY') {
openNaverPayAlertModal('CANCEL');
return;
}
if (['VIRTUAL_ACCOUNT', 'ESCROW_VIRTUAL_ACCOUNT', 'ESCROW_REALTIME_ACCOUNT_TRANSFER'].includes(payType)) {
const payTypeLabel = {
VIRTUAL_ACCOUNT: '가상계좌',
ESCROW_VIRTUAL_ACCOUNT: '에스크로',
ESCROW_REALTIME_ACCOUNT_TRANSFER: '에스크로',
};
if (canCancelAll) {
EventManager.fire('MODAL_CONFIRM_OPEN', {
noticeType: 'WARNING',
message: `${payTypeLabel[payType]} 결제 건은 전체 취소신청만 가능합니다.
모두 취소신청 하시겠습니까?`,
onConfirm: async () => {
const { isSuccess } = await cancelOrder(orderNo);
if (isSuccess) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'SUCCESS',
message: '전체 주문 취소가 완료되었습니다.',
onClose: () => location.reload(),
});
}
},
});
return;
}
if (
orderStatusType !== 'DEPOSIT_WAIT' &&
['ESCROW_VIRTUAL_ACCOUNT', 'ESCROW_REALTIME_ACCOUNT_TRANSFER'].includes(payType)
) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message:
'에스크로 결제 건은 모든 상품이 결제완료 상태에서만 취소신청이 가능합니다.
취소신청은 1:1문의를 통해 문의해주세요.',
});
return;
}
return;
}
if (canCancelAll && orderOptionCount >= 2) {
EventManager.fire('MODAL_CONFIRM_OPEN', {
hasHeader: true,
noticeType: 'WARNING',
cancelLabel: '선택 부분 취소',
confirmLabel: '즉시 전체 취소',
message: '주문에 포함된 상품을 즉시 전체취소 하시겠습니까?
일부 상품만 부분취소 하시겠습니까?',
onCancel: () => {
location.href = `./claim-form.html?orderOptionNo=${orderOptionNo}&claimType=CANCEL`;
},
onConfirm: async () => {
const { isSuccess } = await cancelOrder(orderNo);
if (isSuccess) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'SUCCESS',
message: '전체 주문 취소가 완료되었습니다.',
onClose: () => location.reload(),
});
}
},
});
} else {
location.href = `./claim-form.html?orderOptionNo=${orderOptionNo}&claimType=CANCEL`;
}
},
EXCHANGE: ({ orderOptionNo, deliverable, payType }) => {
if (payType === 'NAVER_PAY') {
openNaverPayAlertModal('EXCHANGE');
return;
}
if (['ESCROW_VIRTUAL_ACCOUNT', 'ESCROW_REALTIME_ACCOUNT_TRANSFER'].includes(payType)) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message: '에스크로 결제 건은 교환 신청이 불가합니다.
취소/반품 후 재주문으로 처리해 주세요.',
});
return;
}
location.href = `./claim-form.html?orderOptionNo=${orderOptionNo}&claimType=EXCHANGE&deliverable=${deliverable}`;
},
RETURN: ({ orderOptionNo, deliverable, payType }) => {
if (payType === 'NAVER_PAY') {
openNaverPayAlertModal('RETURN');
return;
}
if (['ESCROW_VIRTUAL_ACCOUNT', 'ESCROW_REALTIME_ACCOUNT_TRANSFER'].includes(payType)) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message:
'에스크로 결제 건은 결제업체에서 발송한 이메일에서만 반품신청이 가능합니다.
이메일 수신함을 확인해주세요.',
});
return;
}
location.href = `./claim-form.html?orderOptionNo=${orderOptionNo}&claimType=RETURN&deliverable=${deliverable}`;
},
WITHDRAW_CANCEL: ({ orderOptionNo, claimNo, claimNos }) => {
const claimStatusLabel = '취소';
if (!validateWithdrawClaim({ claimNo, claimNos, claimStatusLabel, orderOptionNo })) {
return;
}
openWithdrawClaimModal(orderOptionNo, claimStatusLabel);
},
WITHDRAW_EXCHANGE: ({ orderOptionNo, claimNo, claimNos }) => {
const claimStatusLabel = '교환';
if (!validateWithdrawClaim({ claimNo, claimNos, claimStatusLabel, orderOptionNo })) {
return;
}
openWithdrawClaimModal(orderOptionNo, claimStatusLabel);
},
WITHDRAW_RETURN: ({ orderOptionNo, claimNo, claimNos }) => {
const claimStatusLabel = '반품';
if (!validateWithdrawClaim({ claimNo, claimNos, claimStatusLabel, orderOptionNo })) {
return;
}
openWithdrawClaimModal(orderOptionNo, claimStatusLabel);
},
VIEW_DELIVERY: ({ deliveryUri, deliveryType }) => {
if (deliveryType === 'DIRECT_DELIVERY') {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'WARNING',
message: '직접배송의 경우 배송조회를 지원하지 않습니다.
판매자에게 문의해주세요.',
});
return;
}
openDeliveryTracking(deliveryUri);
},
CONFIRM_ORDER: ({ orderOptionNo, payType, orderStatusType }) => {
if (payType === 'NAVER_PAY') {
openNaverPayAlertModal('CONFIRM_ORDER', orderStatusType);
return;
}
EventManager.fire('MODAL_CONFIRM_OPEN', {
noticeType: 'WARNING',
message: '구매확정 하시겠습니까?
확정 후에는 교환·반품 신청이 어렵습니다.',
cancelLabel: '취소',
confirmLabel: '확인',
onConfirm: async () => {
const { isSuccess } = await confirmOrder(orderOptionNo);
if (isSuccess) {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'SUCCESS',
message: '구매확정 처리되었습니다.',
onClose: () => location.reload(),
});
}
},
});
},
WRITE_REVIEW: ({ payType, productNo, orderStatusType }) => {
if (payType === 'NAVER_PAY') {
openNaverPayAlertModal('WRITE_REVIEW', orderStatusType);
return;
}
const openForm = () => EventManager.fire('OPEN_LAYER_MODAL', {
name: 'product-review-form',
title: '상품후기',
modalAddClass: 'product-review-form-modal',
isFull: false,
data: {
productNo,
isModify: false,
confirmedOrderReceipt: true,
},
onClose: ({ reason }) => {
if (reason === 'DID_SUBMIT') {
EventManager.fire('MODAL_ALERT_OPEN', {
noticeType: 'success',
message: '상품후기가 등록되었습니다.',
onClose: () => {
location.reload();
},
});
}
},
});
confirmOrderReceipt(orderStatusType, openForm);
},
VIEW_CLAIM_DETAIL: ({ claimNo, modalTitlePrefix }) => {
EventManager.fire('OPEN_LAYER_MODAL', {
name: 'claim-detail',
title: `${modalTitlePrefix ?? '클레임'} 내역 상세`,
isFull: false,
data: {
claimNo,
},
});
},
};
const dateSearchModule = document.querySelector('date-search');
const dateSearchClickEventListener = ({ target }) => {
const actionTarget = target.getAttribute('shopby-action');
if (!actionTarget) {
return;
}
MODULE_ACTION_HANDLER[actionTarget]();
};
dateSearchModule?.addEventListener('click', dateSearchClickEventListener);
const orderProductOptionsModule = document.querySelector('my-orders');
const orderProductOptionsClickEventListener = ({ target }) => {
const actionTarget = target.getAttribute('shopby-action');
if (!actionTarget) {
return;
}
const orderOptionCount = target.closest('[shopby-order-option-count]')?.getAttribute('shopby-order-option-count');
const orderOptionNo = target.closest('[shopby-order-option-no]')?.getAttribute('shopby-order-option-no');
const payType = target.closest('[shopby-pay-type]')?.getAttribute('shopby-pay-type');
const orderStatusType = target.closest('[shopby-order-status]')?.getAttribute('shopby-order-status');
const deliverable = target.getAttribute('shopby-deliverable');
const deliveryUri = target.getAttribute('shopby-delivery-uri');
const claimNo = target.getAttribute('shopby-claim-no');
const modalTitlePrefix = target.getAttribute('shopby-modal-title-prefix');
const productNo = target.getAttribute('shopby-product-no');
const orderNo = target.getAttribute('shopby-order-no');
const deliveryType = target.getAttribute('shopby-delivery-type');
const claimNos = target.closest('[shopby-claim-nos]')?.getAttribute('shopby-claim-nos');
const orderNextActionType = target
.closest('[shopby-order-next-action-type]')
?.getAttribute('shopby-order-next-action-type');
MODULE_ACTION_HANDLER[actionTarget]({
orderOptionCount,
orderOptionNo,
deliverable,
deliveryUri,
claimNo,
modalTitlePrefix,
productNo,
orderNo,
payType,
orderStatusType,
deliveryType,
claimNos,
orderNextActionType,
});
};
orderProductOptionsModule.addEventListener('click', orderProductOptionsClickEventListener);
// 주문 상품 개별 "장바구니 담기" — 주문 옵션(productNo/optionNo/orderCnt)을 그대로 장바구니에 추가
const addOrderOptionToCart = ({ productNo, optionNo, orderCnt }) => {
const cartAction = ShopbySkin.actions.getCartAction();
return cartAction.addToCart([
{ productNo: Number(productNo), optionNo: Number(optionNo), orderCnt: Number(orderCnt) || 1 },
]);
};
const orderCartAddClickListener = async ({ target }) => {
const btn = target.closest('.gb-cart-add');
if (!btn || btn.disabled) {
return;
}
const productNo = btn.getAttribute('data-product-no');
const optionNo = btn.getAttribute('data-option-no');
const orderCnt = btn.getAttribute('data-order-cnt');
// 옵션 번호가 없으면(구옵션/삭제 등) 상품 상세로 이동해 옵션을 다시 선택하도록 유도
if (!optionNo || Number.isNaN(Number(optionNo)) || Number(optionNo) <= 0) {
location.href = `/pages/product/product-detail.html?productNo=${productNo}`;
return;
}
btn.disabled = true;
try {
await addOrderOptionToCart({ productNo, optionNo, orderCnt });
EventManager.fire('MODAL_CONFIRM_OPEN', {
message: '
상품이 장바구니에 담겼습니다. 바로 확인하시겠습니까?
', cancelLabel: '쇼핑계속하기', confirmLabel: '장바구니 가기', onConfirm: () => { location.href = '/pages/order/cart.html'; }, }); } catch (e) { EventManager.fire('MODAL_ALERT_OPEN', { noticeType: 'WARNING', message: '품절되었거나 담을 수 없는 상품입니다.', }); } finally { btn.disabled = false; } }; orderProductOptionsModule.addEventListener('click', orderCartAddClickListener); // 주문 상품별 액션 정리 — 배송조회는 상태 라벨 옆 링크로, 교환·반품은 한 버튼으로 묶고 // 노출 순서를 [교환·반품] [구매 확정] [후기 작성] 로 고정한다. const moveDeliveryLinkToStatus = (item, actionBox) => { const deliveryBtn = actionBox.querySelector('[shopby-action="VIEW_DELIVERY"]'); const statusBox = item.querySelector('.order-card__status'); if (!deliveryBtn || !statusBox || statusBox.querySelector('.gb-delivery-link')) { return; } const link = document.createElement('button'); link.type = 'button'; link.className = 'gb-delivery-link'; link.textContent = '배송 조회'; ['shopby-action', 'shopby-delivery-uri', 'shopby-delivery-type'].forEach((name) => { const value = deliveryBtn.getAttribute(name); if (value !== null) { link.setAttribute(name, value); } }); statusBox.appendChild(link); deliveryBtn.remove(); }; const mergeClaimButtons = (actionBox) => { const claimButtons = ['EXCHANGE', 'RETURN'] .map((action) => actionBox.querySelector(`[shopby-action="${action}"]`)) .filter(Boolean); if (!claimButtons.length) { return; } claimButtons.forEach((btn) => btn.classList.add('gb-claim-src')); // 배송 전(결제완료·상품준비중·배송준비중)에는 서버가 EXCHANGE만 내려준다. // 아직 받지 않은 상품이라 교환이 성립하지 않으므로 취소 신청만 남기고 묶음 버튼을 만들지 않는다. if (!actionBox.querySelector('[shopby-action="RETURN"]')) { return; } const combo = document.createElement('button'); combo.type = 'button'; combo.className = 'btn btn--line-gray btn--xs gb-claim-combo'; combo.textContent = '교환·반품'; actionBox.appendChild(combo); }; const fixActionOrder = (actionBox) => { ['.gb-claim-combo', '[shopby-action="CONFIRM_ORDER"]', '[shopby-action="WRITE_REVIEW"]'] .map((selector) => actionBox.querySelector(selector)) .filter(Boolean) .forEach((btn) => actionBox.appendChild(btn)); }; // 액션 영역이 상품 영역의 형제인지 자식인지가 템플릿 버전마다 달라 양쪽을 모두 훑는다 const findOrderItem = (actionBox) => actionBox.closest('.order-card__item') ?? actionBox.parentElement?.querySelector('.order-card__item') ?? null; const arrangeOrderActions = () => { document.querySelectorAll('.order-card__actions').forEach((actionBox) => { const item = findOrderItem(actionBox); if (!item || actionBox.dataset.gbArranged === 'Y') { return; } moveDeliveryLinkToStatus(item, actionBox); mergeClaimButtons(actionBox); fixActionOrder(actionBox); actionBox.dataset.gbArranged = 'Y'; }); }; new MutationObserver(arrangeOrderActions).observe(orderProductOptionsModule, { childList: true, subtree: true, }); arrangeOrderActions(); // [교환·반품] 클릭 시 어떤 주문 건인지 보여주고 교환/반품을 고르는 모달 const closeClaimModal = () => document.querySelector('.gb-claim-modal')?.remove(); const openClaimModal = (comboBtn) => { const actionBox = comboBtn.closest('.order-card__actions'); const item = findOrderItem(actionBox); const sources = ['EXCHANGE', 'RETURN'] .map((action) => actionBox.querySelector(`.gb-claim-src[shopby-action="${action}"]`)) .filter(Boolean); const modal = document.createElement('div'); modal.className = 'gb-claim-modal'; modal.innerHTML = `