[251017] feat: SearchPanel.new.v2.jsx
🕐 커밋 시간: 2025. 10. 17. 20:08:29 📊 변경 통계: • 총 파일: 1개 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx (javascript): ✅ Added: SearchResultsNew(), getButtonTabList(), upBtnClick()
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
import downBtnImg from '../../../assets/images/btn/search_btn_down_arrow.png';
|
||||
import upBtnImg from '../../../assets/images/btn/search_btn_up_arrow.png';
|
||||
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
|
||||
import hotPicksBrandImage from '../../../assets/images/searchpanel/img-search-hotpicks.png';
|
||||
import CustomImage from '../../components/CustomImage/CustomImage';
|
||||
import TButtonTab, { LIST_TYPE } from '../../components/TButtonTab/TButtonTab';
|
||||
import TDropDown from '../../components/TDropDown/TDropDown';
|
||||
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
|
||||
import { $L } from '../../utils/helperMethods';
|
||||
import { SpotlightIds } from '../../utils/SpotlightIds';
|
||||
import css from './SearchResults.new.module.less';
|
||||
import ItemCard from './SearchResultsNew/ItemCard';
|
||||
import ShowCard from './SearchResultsNew/ShowCard';
|
||||
|
||||
const ITEMS_PER_PAGE = 10;
|
||||
|
||||
const SearchResultsNew = ({ itemInfo, showInfo, themeInfo, shopperHouseInfo, keywordClick }) => {
|
||||
// ShopperHouse 데이터를 ItemCard 형식으로 변환
|
||||
const convertedShopperHouseItems = useMemo(() => {
|
||||
if (!shopperHouseInfo || !shopperHouseInfo.results || shopperHouseInfo.results.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const docs = shopperHouseInfo.results[0].docs || [];
|
||||
return docs.map((doc, index) => {
|
||||
const contentId = doc.contentId;
|
||||
const tokens = contentId.split('_');
|
||||
const patnrId = tokens?.[4] || '';
|
||||
const prdtId = tokens?.[5] || '';
|
||||
|
||||
return {
|
||||
thumbnail: doc.thumnail || doc.imgPath || '', //이미지 경로
|
||||
title: doc.title || doc.prdtName || '', // 제목
|
||||
dcPrice: doc.dcPrice || doc.price || '', // 할인가격
|
||||
price: doc.orgPrice || doc.price || '', // 원가
|
||||
soldout: doc.soldout || false, // 품절 여부
|
||||
contentId, //콘텐트 아이디
|
||||
reviewGrade: doc.reviewGrade || '', //리뷰 점수 (추가 정보)
|
||||
partnerName: doc.partnerName || '', //파트너 네임
|
||||
patnrId, // 파트너 아이디
|
||||
prdtId, // 상품 아이디
|
||||
};
|
||||
});
|
||||
}, [shopperHouseInfo]);
|
||||
const getButtonTabList = () => {
|
||||
// ShopperHouse 데이터가 있으면 그것을 사용, 없으면 기존 검색 결과 사용
|
||||
const itemLength = convertedShopperHouseItems?.length || itemInfo?.length || 0;
|
||||
const showLength = showInfo?.length || 0;
|
||||
|
||||
return [
|
||||
itemLength && $L(`ITEM (${itemLength})`),
|
||||
showLength && $L(`SHOWS (${showLength})`),
|
||||
].filter(Boolean);
|
||||
};
|
||||
|
||||
let buttonTabList = null;
|
||||
|
||||
//탭
|
||||
const [tab, setTab] = useState(0);
|
||||
//드롭다운
|
||||
const [dropDownTab, setDropDownTab] = useState(0);
|
||||
//표시할 아이템 개수
|
||||
const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
|
||||
|
||||
const [styleChange, setStyleChange] = useState(false);
|
||||
const [filterMethods, setFilterMethods] = useState([]);
|
||||
const cbChangePageRef = useRef(null);
|
||||
|
||||
if (!buttonTabList) {
|
||||
buttonTabList = getButtonTabList();
|
||||
}
|
||||
|
||||
// 현재 탭의 데이터 가져오기 - ShopperHouse 데이터 우선
|
||||
const currentData = tab === 0 ? convertedShopperHouseItems || itemInfo : showInfo;
|
||||
|
||||
// 표시할 데이터 (처음부터 visibleCount 개수만큼)
|
||||
const displayedData = useMemo(() => {
|
||||
if (!currentData) return [];
|
||||
return currentData.slice(0, visibleCount);
|
||||
}, [currentData, visibleCount]);
|
||||
|
||||
// 더 불러올 데이터가 있는지 확인
|
||||
const hasMore = currentData && visibleCount < currentData.length;
|
||||
|
||||
const handleStyle = useCallback(() => {
|
||||
setStyleChange(true);
|
||||
}, []);
|
||||
|
||||
const handleStyleOut = useCallback(() => {
|
||||
setStyleChange(false);
|
||||
}, []);
|
||||
|
||||
//탭 클릭
|
||||
const handleButtonTabClick = useCallback(
|
||||
({ index }) => {
|
||||
if (index === tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTab(index);
|
||||
setVisibleCount(ITEMS_PER_PAGE); // 탭 변경시 표시 개수 리셋
|
||||
if (cbChangePageRef.current) {
|
||||
cbChangePageRef.current(0, false, false);
|
||||
}
|
||||
},
|
||||
[tab]
|
||||
);
|
||||
|
||||
//필터선택
|
||||
const handleSelectFilter = useCallback(
|
||||
({ selected }) => {
|
||||
if (selected === dropDownTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDropDownTab(selected);
|
||||
setVisibleCount(ITEMS_PER_PAGE); // 필터 변경시 표시 개수 리셋
|
||||
},
|
||||
[dropDownTab]
|
||||
);
|
||||
|
||||
const SpottableLi = Spottable('li');
|
||||
const SpottableDiv = Spottable('div');
|
||||
|
||||
// 맨 처음으로 이동 (위 버튼)
|
||||
const upBtnClick = () => {
|
||||
if (cbChangePageRef.current) {
|
||||
cbChangePageRef.current(0, true);
|
||||
}
|
||||
};
|
||||
|
||||
// 10개씩 추가 로드 (아래 버튼)
|
||||
const downBtnClick = useCallback(() => {
|
||||
if (hasMore) {
|
||||
setVisibleCount((prev) => prev + ITEMS_PER_PAGE);
|
||||
}
|
||||
}, [hasMore]);
|
||||
|
||||
// ProductCard 컴포넌트
|
||||
const renderItem = useCallback(
|
||||
({ index, ...rest }) => {
|
||||
const { bgImgPath, title, partnerLogo, partnerName, keyword } = themeInfo[index];
|
||||
return (
|
||||
<SpottableDiv
|
||||
key={`searchProduct-${index}`}
|
||||
className={css.productCard}
|
||||
spotlightId={`searchProduct-${index}`}
|
||||
{...rest}
|
||||
>
|
||||
<div className={css.productImageWrapper}>
|
||||
<img src={bgImgPath} alt={title} className={css.productImage} />
|
||||
</div>
|
||||
<div className={css.productInfo}>
|
||||
<div className={css.productBrandWrapper}>
|
||||
<img src={partnerLogo} alt={partnerName} className={css.brandLogo} />
|
||||
</div>
|
||||
<div className={css.productDetails}>
|
||||
{keyword && (
|
||||
<div className={css.brandName}>
|
||||
{keyword.map((item, index) => (
|
||||
<span key={index}># {item}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className={css.productTitle}>{title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</SpottableDiv>
|
||||
);
|
||||
},
|
||||
[themeInfo]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={css.searchBox}>
|
||||
<div className={css.topBox}>
|
||||
<span className={css.topBoxTitle}>How about these?</span>
|
||||
<ul className={css.topBoxList}>
|
||||
<SpottableLi className={css.topBoxListItem} onClick={() => keywordClick('Puppy food')}>
|
||||
Puppy food
|
||||
</SpottableLi>
|
||||
<SpottableLi className={css.topBoxListItem} onClick={() => keywordClick('Dog toy')}>
|
||||
Dog toy
|
||||
</SpottableLi>
|
||||
<SpottableLi className={css.topBoxListItem} onClick={() => keywordClick('Fitness')}>
|
||||
Fitness
|
||||
</SpottableLi>
|
||||
</ul>
|
||||
</div>
|
||||
{themeInfo && themeInfo?.length > 0 && (
|
||||
<div
|
||||
className={css.hotpicksSection}
|
||||
data-wheel-point={true}
|
||||
spotlightId={'hot-picks-section'}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Hot Picks ({themeInfo?.length})</div>
|
||||
</div>
|
||||
<div className={css.productList}>
|
||||
<TVirtualGridList
|
||||
dataSize={themeInfo?.length}
|
||||
direction="horizontal"
|
||||
renderItem={renderItem}
|
||||
itemWidth={416}
|
||||
itemHeight={436}
|
||||
spacing={20}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={css.itemBox} cbChangePageRef={cbChangePageRef}>
|
||||
<div className={css.tabContainer}>
|
||||
<TButtonTab
|
||||
contents={buttonTabList}
|
||||
onItemClick={handleButtonTabClick}
|
||||
selectedIndex={tab}
|
||||
listType={LIST_TYPE.medium}
|
||||
spotlightId={SpotlightIds.SEARCH_TAB_CONTAINER}
|
||||
/>
|
||||
{tab === 0 && (
|
||||
<TDropDown
|
||||
className={classNames(
|
||||
css.dropdown,
|
||||
styleChange === true ? css.categoryDropdown : null
|
||||
)}
|
||||
onSelect={handleSelectFilter}
|
||||
onOpen={handleStyle}
|
||||
onClose={handleStyleOut}
|
||||
selectedIndex={dropDownTab}
|
||||
width="small"
|
||||
>
|
||||
{filterMethods}
|
||||
</TDropDown>
|
||||
)}
|
||||
</div>
|
||||
{tab === 0 && <ItemCard itemInfo={displayedData} />}
|
||||
{tab === 1 && <ShowCard showInfo={displayedData} />}
|
||||
</div>
|
||||
<div className={css.buttonContainer}>
|
||||
{hasMore && (
|
||||
<SpottableDiv onClick={downBtnClick} className={css.downBtn}>
|
||||
<CustomImage className={css.btnImg} src={downBtnImg} />
|
||||
</SpottableDiv>
|
||||
)}
|
||||
<SpottableDiv onClick={upBtnClick} className={css.upBtn}>
|
||||
<CustomImage className={css.btnImg} src={upBtnImg} />
|
||||
</SpottableDiv>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchResultsNew;
|
||||
Reference in New Issue
Block a user