[251105] fix: BuyNow Option-1
🕐 커밋 시간: 2025. 11. 05. 07:35:13 📊 변경 통계: • 총 파일: 4개 • 추가: +94줄 • 삭제: -70줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx 🔧 주요 변경 내용: • 공통 유틸리티 함수 최적화 • UI 컴포넌트 아키텍처 개선 • 소규모 기능 개선 • 코드 정리 및 최적화
This commit is contained in:
@@ -25,12 +25,36 @@ export const createMockProductOptionData = (originalProductData) => {
|
||||
// 실제 상품 가격 정보 사용 (originalProductData의 priceInfo 또는 기본값)
|
||||
// priceInfo 포맷: "원가|할인가|할인액|할인율|할인율숫자"
|
||||
const priceInfo = originalProductData.priceInfo || '99999|99999|0|0%|0';
|
||||
const [regularPrice = '99999', salePrice = regularPrice] = priceInfo.split('|');
|
||||
|
||||
// 옵션명/옵션 값은 기존 데이터에서 최대한 활용하고, 없으면 기본값 사용
|
||||
const baseOptionName =
|
||||
originalProductData.optNm ||
|
||||
originalProductData.optTpNm ||
|
||||
`${prdtNm} Option`;
|
||||
const optionValueName =
|
||||
originalProductData.optValueNm ||
|
||||
originalProductData.optVlNm ||
|
||||
`${prdtNm} 기본 구성`;
|
||||
|
||||
const optionCode =
|
||||
originalProductData.prodOptCdCval || `MOCK_OPT_${prdtId}_1`;
|
||||
const optionImage =
|
||||
originalProductData.thumbnailUrl960 ||
|
||||
originalProductData.imgUrl ||
|
||||
originalProductData.thumbnailUrl ||
|
||||
null;
|
||||
|
||||
console.log('[BuyNowDataManipulator] createMockProductOptionData - priceInfo:', priceInfo);
|
||||
|
||||
// 옵션 상세 객체
|
||||
const optionDetail = {
|
||||
prodOptCval: `${prdtNm}`,
|
||||
prodOptCdCval: optionCode,
|
||||
prodOptCval: optionValueName,
|
||||
optNm: optionValueName,
|
||||
optPrc: salePrice,
|
||||
optStkQty: 999,
|
||||
optImgUrl: optionImage,
|
||||
priceInfo: priceInfo,
|
||||
stockCnt: 999,
|
||||
soldOutYn: 'N',
|
||||
@@ -41,8 +65,10 @@ export const createMockProductOptionData = (originalProductData) => {
|
||||
return [
|
||||
{
|
||||
// 옵션 ID (기본 값)
|
||||
prodOptSno: `MOCK_OPT_${prdtId}_1`,
|
||||
prodOptTpCdCval: 'BASIC',
|
||||
prodOptSno: optionCode,
|
||||
prodOptTpCdCval: originalProductData.prodOptTpCdCval || 'BASIC',
|
||||
optNm: baseOptionName,
|
||||
optTpNm: originalProductData.optTpNm || baseOptionName,
|
||||
|
||||
// 옵션 상세 목록 (기본값 1개) - prdtOptDtl 배열 구조
|
||||
prdtOptDtl: [optionDetail],
|
||||
|
||||
@@ -141,6 +141,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
(isCancelClick) => (ev) => {
|
||||
fp.pipe(
|
||||
() => {
|
||||
dispatch(clearAllToasts()); // BuyOption Toast 포함 모든 토스트 제거
|
||||
dispatch(finishVideoPreview());
|
||||
dispatch(popPanel(panel_names.DETAIL_PANEL));
|
||||
},
|
||||
|
||||
@@ -136,14 +136,15 @@ const BuyOption = ({
|
||||
// 상품 정보 (props 우선, 없으면 productInfo에서)
|
||||
const selectedPatnrId = propsSelectedPatnrId || productInfo?.patnrId;
|
||||
const selectedPrdtId = propsSelectedPrdtId || productInfo?.prdtId;
|
||||
const isMockMode = BUYNOW_CONFIG.isMockMode();
|
||||
|
||||
// Mock Mode에서 옵션 데이터 처리
|
||||
const productOptionInfos = useMemo(() => {
|
||||
console.log('[BuyOption] productOptionInfos useMemo - isMockMode:', BUYNOW_CONFIG.isMockMode());
|
||||
console.log('[BuyOption] productOptionInfos useMemo - isMockMode:', isMockMode);
|
||||
console.log('[BuyOption] productOptionInfos useMemo - reduxProductOptionInfos:', reduxProductOptionInfos);
|
||||
|
||||
// API Mode: 기존 로직 100% 유지
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
if (!isMockMode) {
|
||||
console.log('[BuyOption] API Mode - using reduxProductOptionInfos');
|
||||
return reduxProductOptionInfos;
|
||||
}
|
||||
@@ -162,7 +163,9 @@ const BuyOption = ({
|
||||
// Mock Mode이고 유효한 옵션 데이터가 있으면 그대로 사용
|
||||
console.log('[BuyOption] Mock Mode - using existing valid reduxProductOptionInfos');
|
||||
return reduxProductOptionInfos;
|
||||
}, [reduxProductOptionInfos, productData]); // logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
|
||||
}, [reduxProductOptionInfos, productData, isMockMode]); // logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
|
||||
const hasMockOptions =
|
||||
isMockMode && Array.isArray(productOptionInfos) && productOptionInfos.length > 0;
|
||||
const logInfo = useMemo(() => {
|
||||
if (productData) {
|
||||
// productData가 있으면 SingleOption과 동일하게 처리
|
||||
@@ -307,7 +310,7 @@ const BuyOption = ({
|
||||
// Mock Mode: API 호출 스킵 (Mock 데이터만 사용)
|
||||
useEffect(() => {
|
||||
// API Mode: 실제 API 호출
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
if (!isMockMode) {
|
||||
dispatch(
|
||||
getProductOption({
|
||||
patnrId: selectedPatnrId,
|
||||
@@ -324,41 +327,35 @@ const BuyOption = ({
|
||||
);
|
||||
}
|
||||
// Mock Mode: API 호출 하지 않음
|
||||
}, [dispatch, selectedPatnrId, selectedPrdtId, userNumber]);
|
||||
}, [dispatch, selectedPatnrId, selectedPrdtId, userNumber, isMockMode]);
|
||||
|
||||
// 포커스 관리 로직 (SingleOption과 유사)
|
||||
useEffect(() => {
|
||||
// if (!isSpotlight) {
|
||||
// // isSpotlight이 false면 일반적인 BuyOption 포커스
|
||||
// console.log('[BuyOption] Component mounted - focusing BUY NOW button');
|
||||
const shouldHandleOptionFocus =
|
||||
productInfo?.optProdYn === 'Y' || hasMockOptions;
|
||||
|
||||
// Spotlight.focus('buy-option-buy-now-button');
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// isSpotlight이 true이고 SingleOption 동작이 필요한 경우 요부분
|
||||
if (productInfo?.optProdYn === 'N') {
|
||||
if (!shouldHandleOptionFocus) {
|
||||
Spotlight.focus('buy-option-buy-now-button');
|
||||
} else if (productInfo?.optProdYn === 'Y') {
|
||||
if(
|
||||
productOptionInfos &&
|
||||
productOptionInfos?.length > 1
|
||||
){
|
||||
Spotlight.focus('buy-option-first-dropdown');
|
||||
} else if (
|
||||
productOptionInfos?.legnth > 0 &&
|
||||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
|
||||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 1
|
||||
){
|
||||
Spotlight.focus('buy-option-second-dropdown');
|
||||
} else {
|
||||
Spotlight.focus('buy-option-buy-now-button');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (productOptionInfos && productOptionInfos.length > 1) {
|
||||
Spotlight.focus('buy-option-first-dropdown');
|
||||
} else if (
|
||||
productOptionInfos &&
|
||||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
|
||||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 1
|
||||
) {
|
||||
Spotlight.focus('buy-option-second-dropdown');
|
||||
} else {
|
||||
Spotlight.focus('buy-option-buy-now-button');
|
||||
}
|
||||
}, [productOptionInfos?.length, productInfo?.length]);
|
||||
}, [
|
||||
productOptionInfos,
|
||||
productInfo?.optProdYn,
|
||||
hasMockOptions,
|
||||
selectedBtnOptIdx,
|
||||
]);
|
||||
|
||||
// checkOutValidate 콜백 함수 (SingleOption과 동일한 로직)
|
||||
function checkOutValidate(response) {
|
||||
@@ -1318,7 +1315,7 @@ const BuyOption = ({
|
||||
{/* 동적 옵션 렌더링 */}
|
||||
{productOptionInfos &&
|
||||
productOptionInfos?.length > 0 &&
|
||||
productInfo?.optProdYn === 'Y' && (
|
||||
(productInfo?.optProdYn === 'Y' || hasMockOptions) && (
|
||||
<>
|
||||
{/* 첫번째 옵션 (여러 옵션이 있을 때만) */}
|
||||
{productOptionInfos?.length > 1 && (
|
||||
|
||||
@@ -213,7 +213,7 @@ const VoiceInputOverlay = ({
|
||||
const [isSilenceCheckActive, setIsSilenceCheckActive] = useState(false);
|
||||
const [hasReached5Chars, setHasReached5Chars] = useState(false); // 처음 5글자 도달 추적
|
||||
// 💬 Bubble 버튼 상태 (true: 버튼 O, false: 텍스트만)
|
||||
const [isBubbleButton, setIsBubbleButton] = useState(true); // 첫 발화때는 true (Try Saying)
|
||||
const [isBubbleButton, setIsBubbleButton] = useState(false); // 첫 발화때는 true (Try Saying)
|
||||
// useSearchHistory Hook 적용 (음성검색 기록 관리)
|
||||
const { addVoiceSearch } = useSearchHistory();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user