(async () => { const { pageHelper, EventManager, utils, constants, actions } = ShopbySkin; const { initialize } = pageHelper.productListHelper(); const $skeleton = $('skeleton'); // 스켈레톤 컴포넌트 const $productList = $('product-list'); // 새로운 상품 리스트 컴포넌트 const $productItem = $('product-item'); // 더보기 클릭시 붙여넣을 상품 리스트 컴포넌트 const defaultPageSize = 50; // pageSize 디폴트 값 const urlQueryParams = utils.getQueryParams(); initialize({ ...urlQueryParams, }); pushState({ orderBy: 'RECENT_PRODUCT' }); pushState({ pageSize: defaultPageSize }); const url = new URL(window.location.href); const keywords = url.searchParams.get('keywords'); // [Jay] 스켈레톤 렌더 const skeletonHtml = renderHtml($skeleton); $skeleton.append(skeletonHtml); // 추출할 갯수(전역) // 리스트 추출 const searchList = await fnGetProductList(url); const totalCountAndSortEl = document.querySelector('total-count-and-sort'); // [Aaron] 총 개수 state 변경 totalCountAndSortEl.store.attach('totalCount', state => { state.totalCount = defaultPageSize; totalCountAndSortEl.updateSlot(state, 'totalCount'); }) // 총 상품 갯수(전역) const totalCount = searchList.result.totalCount; const pageCount = searchList.result.pageCount; const searchListData = []; searchListData.items = searchList.result.items; // [Jay] 상품 존재 여부에 따른 noData 플래그값 설정 searchListData.items.length === 0 ? (searchListData.hasNoItems = true) : (searchListData.hasNoItems = false); const productListHtml = renderHtml($productList, searchListData); $productList.find('section').empty(); $productList.append(productListHtml); // [Jay] 검색 갯수 변경 // 스켈레톤 제거 $('#skeleton').remove(); const currentPage = url.searchParams.get('pageNumber') ?? 1; // 현재 페이지 번호 const isEndPage = pageCount === currentPage; // 마지막 페이지 if (isEndPage) { $('#searchMoreBtn').remove(); // 더보기 버튼 제거 } // Velocity.js로 fadeIn 애니메이션 적용 $productList.css('opacity', '0'); $productList.velocity({ opacity: 1 }, { duration: 500 }); const productListEl = document.querySelector('product-list'); const totalCountSortEl = document.querySelector('total-count-and-sort'); // 카테고리 필터 선택자 const displayProductActionMap = { // NOTE: 좋아요버튼 클릭 PRODUCT_LIKE_BTN: ({ event, btn }) => { event?.preventDefault?.(); const productNo = Number(btn.getAttribute('shopby-product-no') ?? 0); utils.isSignIn() ? EventManager.fire('MUTATE_LIKE_PRODUCT', { productNo, onSuccess: (like) => { like ? btn.classList.add('is-active') : btn.classList.remove('is-active'); }, }) : EventManager.fire('MODAL_CONFIRM_OPEN', { message: `로그인하셔야 본 서비스를 이용하실 수 있습니다.`, noticeType: constants.NOTICE_MODAL_TYPE.WARNING, onConfirm: () => { const nextUrl = `${location.origin}/pages/sign-in/sign-in.html`; location.href = `${nextUrl}?from=${encodeURIComponent(location.href)}`; }, }); }, // NOTE: 더보기 SEARCH_MORE_PRODUCT: () => { EventManager.fire('SEARCH_MORE_PRODUCT'); }, // NOTE: 상품터치시 상품상세로 이동전 성인상품인지 확인을 위한 이벤트 MOVE_PRODUCT_DETAIL: ({ event }) => { if (!utils.isAdultVerified(event)) { EventManager.fire('MODAL_CONFIRM_OPEN', { message: '성인인증이 필요한 상품입니다.', noticeType: 'CAUTION', onConfirm: () => { location.href = '/pages/intro/adult-certification.html'; }, }); } }, OPEN_FILTER: async ({ event }) => { EventManager.fire('OPEN_LAYER_MODAL', { name: 'product-category-filter-form', title: 'FILTER', isFull: true, }); }, PRODUCT_SEARCH: async ({ event }) => { await fnMoreProductSearch(); }, CHANGE_SORT_TYPE: async ({ event }) => { // [Jay] 스켈레톤 렌더 const skeletonHtml = renderHtml($skeleton); $skeleton.append(skeletonHtml); const url = new URL(window.location.href); // 선택된 sortType Set const sortType = $('#sortType option:selected').val(); url.searchParams.set('sortType', sortType); // 리스트 추출 const searchList = await fnGetProductList(url); // 총 상품 갯수(전역) const pageCount = searchList.result.pageCount; const searchListData = []; searchListData.items = searchList.result.items; // [Jay] 상품 존재 여부에 따른 noData 플래그값 설정 searchListData.items.length === 0 ? (searchListData.hasNoItems = true) : (searchListData.hasNoItems = false); // [Jay] 검색 페이지 판별값 keywords ? (searchListData.isSearchPage = true) : (searchListData.isSearchPage = false); const productListHtml = renderHtml($productList, searchListData); // 기존 상품리스트 제거 $productList.find('section').remove(); $productList.append(productListHtml); // 스켈레톤 제거 $('#skeleton').remove(); const currentPage = url.searchParams.get('pageNumber') ?? 1; // 현재 페이지 번호 const isEndPage = pageCount === currentPage; // 마지막 페이지 if (isEndPage) { $('#searchMoreBtn').remove(); // 더보기 버튼 제거 } // Velocity.js로 fadeIn 애니메이션 적용 $productList.css('opacity', '0'); $productList.velocity({ opacity: 1 }, { duration: 500 }); }, // [KR] 브레드크럼 뒤로가기 이벤트 추가 BACK: async ({ event }) => { if (history.state && history.state.idx === 0) { location.href = '/'; return; } if (!document.referrer || location.href === document.referrer) { history.back(); return; } location.href = document.referrer; }, }; const clickEventListener = (event) => { if (!event?.target) { return; } const btn = event?.target?.closest?.('[shopby-action]') || event?.target?.parentElement?.closest?.('[shopby-action]'); const action = btn?.getAttribute('shopby-action'); if (!btn) { return; } displayProductActionMap[action]?.({ btn, event, }); }; const changeEventListener = (e) => { const url = new URL(window.location.href); const params = new URLSearchParams(url.search); // 기존 URL에서 파라미터를 가져옴 if (!e?.target) { return; } const btn = e?.target?.closest?.('[shopby-action]') || e?.target?.parentElement?.closest?.('[shopby-action]'); // 브라우저의 주소창을 업데이트 (페이지 리로드 없음) // window.history.pushState({}, '', url); let action = 'PRODUCT_SEARCH'; if (e.target.getAttribute('id') === 'sortType') { action = 'CHANGE_SORT_TYPE'; } // [Jay] 카테고리 필터 체크박스, 라디오 이벤트 버블링 현상 막음 if ( e.target.getAttribute('type') !== 'checkbox' && e.target.getAttribute('type') !== 'radio' ) { displayProductActionMap[action]?.({ event: e, }); } if (!btn) { return; } }; productListEl.addEventListener('click', clickEventListener); document.addEventListener('change', changeEventListener); totalCountSortEl.addEventListener('click', clickEventListener); /** * [Jay] 상품 리스트 호출 함수 * @param url * @param isMoreList * @returns {Promise|Promise<*|{result: *, statusCode: *}|undefined>|*>} */ async function fnGetProductList(url, isMoreList = false) { let pageNumber = 1; // 더보기인 경우 if (isMoreList) { const currentPage = $('.product-section__more').data('page-number') ?? 1; // 현재 페이지 번호 const nextPage = Number(currentPage) + 1; // 다음 페이지 번호 pageNumber = nextPage; // next pageNumber set $('.product-section__more').data('page-number', nextPage); $('.product-section__more').attr('data-page-number', nextPage); } const orderBy = url.searchParams.get('orderBy') const searchParams = { queryString: { pageNumber: pageNumber, pageSize: defaultPageSize, 'order.by': orderBy, 'order.direction': 'DESC', 'filter.saleStatus': 'ALL_CONDITIONS', 'filter.soldout': false, 'filter.totalReviewCount': true, hasTotalCount: true, 'excludeCategoryNos': '846988' }, }; return callShopApi('/products/search?', 'GET', searchParams); } /** * 상품 더보기 추출 및 렌더 함수 * @returns {Promise} */ async function fnMoreProductSearch() { const url = new URL(window.location.href); // 스켈레톤 추가 $productList.find('.product-list__items').after(skeletonHtml); // 상품 데이터 추출 const searchList = await fnGetProductList(url, true); const searchListData = []; searchListData.items = searchList.result.items; const productItemsHtml = renderHtml($productItem, searchListData); $productList.find('.product-list__items').append(productItemsHtml); // [Aaron] 다음 페이지 상품 개수가 지정된 pageSize 이하일 경우 더보기 버튼 제거 if (searchListData.items.length <= defaultPageSize) document.getElementById('searchMoreBtn').remove(); // 스켈레톤 제거 $('#skeleton').remove(); // 마지막 페이지 const currentPage = $('.product-section__more').data('page-number') ?? 1; // 현재 페이지 번호 const isEndPage = totalCount / defaultPageSize === currentPage; if (isEndPage) { $('#searchMoreBtn').remove(); // 더보기 버튼 제거 } // Velocity.js로 fadeIn 애니메이션 적용 $('.thumb-item__link').velocity({ opacity: 1 }, { duration: 500 }); } /** * [Jay] 상품 정렬 추출 함수 * @param sortType * @param url * @returns {{orderBy: null, direction: null}} */ function fnOrderBy(sortType, url) { let orderBy = null; let direction = null; if (sortType === 'SALE_CNT') { orderBy = 'POPULAR'; } else if (sortType === 'LOW_PRICE') { orderBy = 'DISCOUNTED_PRICE'; direction = 'ASC'; } else if (sortType === 'HIGH_PRICE') { orderBy = 'DISCOUNTED_PRICE'; direction = 'DESC'; } else if (sortType === 'REVIEW') { orderBy = 'REVIEW'; direction = 'DESC'; } else if (sortType === 'RECENT_PRODUCT') { orderBy = 'RECENT_PRODUCT'; direction = 'DESC'; } url.searchParams.set('direction', direction); // 브라우저의 주소창을 업데이트 (페이지 리로드 없음) window.history.pushState({}, '', url); return { orderBy, direction }; } })();