diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index 30f5f742..fa86146f 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -6,6 +6,7 @@ export const types = { POP_PANEL: 'POP_PANEL', UPDATE_PANEL: 'UPDATE_PANEL', RESET_PANELS: 'RESET_PANELS', + FOCUS_PANEL: 'FOCUS_PANEL', // ๐ฝ [251114] ๋ช ์์ ํฌ์ปค์ค ์ด๋ // ๐ฝ [์ ๊ท] panel history actions ENQUEUE_PANEL_HISTORY: 'ENQUEUE_PANEL_HISTORY', @@ -82,12 +83,12 @@ export const types = { CLEAR_CART: 'CLEAR_CART', //cart api action GET_MY_INFO_CART_SEARCH: 'GET_MY_INFO_CART_SEARCH', - INSERT_MY_INFO_CART : "INSERT_MY_INFO_CART", - DELETE_MY_INFO_CART : "DELETE_MY_INFO_CART", - DELETE_ALL_MY_INFO_CART : "DELETE_ALL_MY_INFO_CART", - UPDATE_MY_INFO_CART : "UPDATE_MY_INFO_CART", + INSERT_MY_INFO_CART: 'INSERT_MY_INFO_CART', + DELETE_MY_INFO_CART: 'DELETE_MY_INFO_CART', + DELETE_ALL_MY_INFO_CART: 'DELETE_ALL_MY_INFO_CART', + UPDATE_MY_INFO_CART: 'UPDATE_MY_INFO_CART', //cart checkbox toggle action - TOGGLE_CHECK_CART : "TOGGLE_CHECK_CART", + TOGGLE_CHECK_CART: 'TOGGLE_CHECK_CART', // appData actions ADD_MAIN_INDEX: 'ADD_MAIN_INDEX', @@ -315,7 +316,7 @@ export const types = { SET_MODAL_BORDER: 'SET_MODAL_BORDER', SET_BANNER_VISIBILITY: 'SET_BANNER_VISIBILITY', - // ๐ฝ [์ถ๊ฐ] JustForYou ์ํ ๊ด๋ฆฌ ๋ถ๋ถ + // ๐ฝ [์ถ๊ฐ] JustForYou ์ํ ๊ด๋ฆฌ ๋ถ๋ถ JUSTFORYOU: 'JUSTFORYOU', // ๐ฝ Voice Conductor ๊ด๋ จ ์ก์ ํ์ diff --git a/com.twin.app.shoptime/src/actions/mediaActions.js b/com.twin.app.shoptime/src/actions/mediaActions.js index 9b4e4e7b..4d4a04f7 100644 --- a/com.twin.app.shoptime/src/actions/mediaActions.js +++ b/com.twin.app.shoptime/src/actions/mediaActions.js @@ -23,15 +23,16 @@ export const startMediaPlayer = const topPanel = panels[panels.length - 1]; let panelWorkingAction = pushPanel; - console.log('[startMediaPlayer] ========== Called =========='); - console.log('[startMediaPlayer] Current panels:', JSON.stringify(panels, null, 2)); - console.log('[startMediaPlayer] topPanel:', JSON.stringify(topPanel, null, 2)); + console.log('[startMediaPlayer]-LoadingVideo ๐ ์์:', { + showUrl: rest?.showUrl?.substring(0, 50), + showNm: rest?.showNm, + prdtId: rest?.prdtId, + modal, + modalContainerId, + }); if (topPanel && topPanel.name === panel_names.MEDIA_PANEL) { panelWorkingAction = updatePanel; - console.log('[startMediaPlayer] Using updatePanel (existing MediaPanel)'); - } else { - console.log('[startMediaPanel] Using pushPanel (new MediaPanel)'); } const allParams = { @@ -42,8 +43,6 @@ export const startMediaPlayer = ...rest, }; - console.log('[startMediaPlayer] All parameters:', JSON.stringify(allParams, null, 2)); - dispatch( panelWorkingAction( { @@ -54,7 +53,7 @@ export const startMediaPlayer = ) ); - console.log('[startMediaPlayer] Panel action dispatched'); + console.log('[startMediaPlayer]-LoadingVideo โ MediaPanel dispatch ์๋ฃ'); if (modal && modalContainerId && !spotlightDisable) { Spotlight.setPointerMode(false); diff --git a/com.twin.app.shoptime/src/actions/panelActions.js b/com.twin.app.shoptime/src/actions/panelActions.js index 54b2c5e5..2312e85a 100644 --- a/com.twin.app.shoptime/src/actions/panelActions.js +++ b/com.twin.app.shoptime/src/actions/panelActions.js @@ -1,4 +1,5 @@ -import { types } from "./actionTypes"; +import { types } from './actionTypes'; +import Spotlight from '@enact/spotlight'; /* name: panel_names.PLAYER_PANEL, @@ -27,3 +28,85 @@ export const resetPanels = (panels) => ({ type: types.RESET_PANELS, payload: panels, }); + +/** + * [251114] ๋ช ์์ ํฌ์ปค์ค ์ด๋ + * Panel์ ๋น๋๊ธฐ ์์ (useEffect, ํ์ด๋จธ ๋ฑ)์ด ํฌ์ปค์ค๋ฅผ ํ์ทจํ๋ ๊ฒ์ ๋ฐฉ์ง + * @param {string} panelName - ๋์ Panel ์ด๋ฆ + * @param {string} focusTarget - ํฌ์ปค์คํ ์์ ID + * @returns {Function} Redux thunk + */ +export const focusPanel = (panelName, focusTarget) => { + return (dispatch, getState) => { + const state = getState(); + const panels = state.panels.panels; + + console.log('[focusPanel] ํฌ์ปค์ค ์ด๋ ์๋', { + panelName, + focusTarget, + currentPanels: panels.map((p) => p.name), + timestamp: Date.now(), + }); + + // ์์ ์ฑ ์ฒดํฌ 1: Panel์ด ์กด์ฌํ๊ณ ์ต์๋จ ๋๋ ๊ทธ ์๋์ ์๋๊ฐ? + const targetPanelIndex = panels.findIndex((p) => p.name === panelName); + const targetPanel = panels[targetPanelIndex]; + const topPanel = panels[panels.length - 1]; + + if (!targetPanel) { + console.warn(`[focusPanel] โ Panel์ ์ฐพ์ ์ ์์: ${panelName}`); + return; + } + + // Panel์ด ์ต์๋จ ๋๋ ๊ทธ ์๋ ๋ ์ด์ด์ ์๋์ง ํ์ธ + // MediaPanel(์ต์๋จ) ์์ ๋ค๋ฅธ Modal์ด ์๋ ๊ฒฝ์ฐ๋ ํ์ฉํ์ง ์์ + const panelsAboveTarget = panels.slice(targetPanelIndex + 1); + const hasBlockingModalAbove = panelsAboveTarget.some( + (panel) => panel?.panelInfo?.modal === true && panel.name !== panelName + ); + + if (hasBlockingModalAbove) { + const blockingModal = panelsAboveTarget.find((panel) => panel?.panelInfo?.modal === true); + console.warn( + `[focusPanel] โ ๏ธ ์์์ Modal์ด ์์. ` + + `${panelName}(${targetPanelIndex}์ธต)์ ํฌ์ปค์คํ ์ ์์. ` + + `์๋จ Modal: ${blockingModal?.name}(${panelsAboveTarget.indexOf(blockingModal) + targetPanelIndex + 1}์ธต)` + ); + return; + } + + console.log( + `[focusPanel] โ Panel ์์น ํ์ธ: ${panelName}(${targetPanelIndex}์ธต), ` + + `์ ์ฒด Panel: ${panels.length}์ธต` + ); + + // ํฌ์ปค์ค ์ด๋ + setTimeout(() => { + const element = document.getElementById(focusTarget); + + if (!element) { + console.warn(`[focusPanel] โ ์์๋ฅผ ์ฐพ์ ์ ์์: ${focusTarget}`); + return; + } + + if (element.offsetParent === null) { + console.warn(`[focusPanel] โ ๏ธ ์์๊ฐ ์จ๊ฒจ์ ธ์์: ${focusTarget}`); + return; + } + + // โ ํฌ์ปค์ค ์ด๋ + Spotlight.focus(focusTarget); + console.log(`[focusPanel] โ ํฌ์ปค์ค ์ด๋ ์ฑ๊ณต: ${panelName} โ ${focusTarget}`); + + // Reducer์ ๋ฐ์ + dispatch({ + type: types.FOCUS_PANEL, + payload: { + panelName, + focusTarget, + timestamp: Date.now(), + }, + }); + }, 0); + }; +}; diff --git a/com.twin.app.shoptime/src/reducers/panelReducer.js b/com.twin.app.shoptime/src/reducers/panelReducer.js index 50049037..fd16d391 100644 --- a/com.twin.app.shoptime/src/reducers/panelReducer.js +++ b/com.twin.app.shoptime/src/reducers/panelReducer.js @@ -5,6 +5,7 @@ const initialState = { // ๊ธฐ์กด ์ํ - ์์ ํ ํธํ๋จ panels: [], lastPanelAction: '', //"", "push", "pop", "update", "reset", "previewPush", "previewPop", "previewUpdate" + lastFocusTarget: null, // [251114] ๋ง์ง๋ง ํฌ์ปค์ค ๋์ (panelName + elementId) // [251106] ํจ๋ ์ก์ ํ ๊ด๋ จ ์ํ - ๊ธฐ์กด ๊ธฐ๋ฅ์ ์ ํ ์ํฅ ์์ panelActionQueue: [], // ์ฒ๋ฆฌ ๋๊ธฐ ์ค์ธ ํจ๋ ์ก์ ํ @@ -13,7 +14,7 @@ const initialState = { queueStats: { totalProcessed: 0, // ์ด ์ฒ๋ฆฌ๋ ์ก์ ์ failedCount: 0, // ์คํจํ ์ก์ ์ - averageProcessingTime: 0 // ํ๊ท ์ฒ๋ฆฌ ์๊ฐ + averageProcessingTime: 0, // ํ๊ท ์ฒ๋ฆฌ ์๊ฐ }, // [251106] ๋น๋๊ธฐ ์ก์ ๊ด๋ จ ์ํ @@ -30,7 +31,7 @@ export const panelsReducer = (state = initialState, action) => { case types.PUSH_PANEL: { console.log('[panelReducer] ๐ต PUSH_PANEL START', { newPanelName: action.payload.name, - currentPanels: state.panels.map(p => p.name), + currentPanels: state.panels.map((p) => p.name), duplicatable: action.duplicatable, }); @@ -76,7 +77,7 @@ export const panelsReducer = (state = initialState, action) => { } console.log('[panelReducer] ๐ต PUSH_PANEL END', { - resultPanels: newState.map(p => p.name), + resultPanels: newState.map((p) => p.name), lastAction, }); @@ -90,7 +91,7 @@ export const panelsReducer = (state = initialState, action) => { case types.POP_PANEL: { console.log('[panelReducer] ๐ด POP_PANEL START', { targetPanel: action.payload || 'last_panel', - currentPanels: state.panels.map(p => p.name), + currentPanels: state.panels.map((p) => p.name), }); let lastAction = 'pop'; @@ -113,7 +114,7 @@ export const panelsReducer = (state = initialState, action) => { } console.log('[panelReducer] ๐ด POP_PANEL END', { - resultPanels: resultPanels.map(p => p.name), + resultPanels: resultPanels.map((p) => p.name), lastAction, }); @@ -159,7 +160,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.RESET_PANELS: { console.log('[panelReducer] ๐ข RESET_PANELS START', { - currentPanels: state.panels.map(p => p.name), + currentPanels: state.panels.map((p) => p.name), payloadProvided: !!action.payload, }); @@ -171,7 +172,7 @@ export const panelsReducer = (state = initialState, action) => { : []; console.log('[panelReducer] ๐ข RESET_PANELS END', { - resultPanels: updatedPanels.map(p => p.name), + resultPanels: updatedPanels.map((p) => p.name), }); return { @@ -267,7 +268,8 @@ export const panelsReducer = (state = initialState, action) => { const processingTime = Date.now() - startTime; const newTotalProcessed = state.queueStats.totalProcessed + 1; const newAverageTime = - (state.queueStats.averageProcessingTime * state.queueStats.totalProcessed + processingTime) / + (state.queueStats.averageProcessingTime * state.queueStats.totalProcessed + + processingTime) / newTotalProcessed; console.log('[panelReducer] โ QUEUE_ITEM_PROCESSED', { @@ -352,7 +354,7 @@ export const panelsReducer = (state = initialState, action) => { ...action.payload, status: 'running', startTime: Date.now(), - } + }, }, queueError: null, // ์๋ฌ ์ด๊ธฐํ }; @@ -381,16 +383,19 @@ export const panelsReducer = (state = initialState, action) => { result: action.payload.result, executionTime, completedAt: action.payload.timestamp, - } + }, ].slice(-100), // ์ต๊ทผ 100๊ฐ๋ง ์ ์ง queueError: null, queueStats: { ...state.queueStats, totalProcessed: state.queueStats.totalProcessed + 1, - averageProcessingTime: Math.round( - ((state.queueStats.averageProcessingTime * state.queueStats.totalProcessed) + executionTime) / - (state.queueStats.totalProcessed + 1) * 100 - ) / 100, + averageProcessingTime: + Math.round( + ((state.queueStats.averageProcessingTime * state.queueStats.totalProcessed + + executionTime) / + (state.queueStats.totalProcessed + 1)) * + 100 + ) / 100, }, }; } @@ -419,7 +424,7 @@ export const panelsReducer = (state = initialState, action) => { error: action.payload.error, executionTime, failedAt: action.payload.timestamp, - } + }, ].slice(-100), // ์ต๊ทผ 100๊ฐ๋ง ์ ์ง queueError: { actionId: action.payload.actionId, @@ -433,6 +438,24 @@ export const panelsReducer = (state = initialState, action) => { }; } + // [251114] ๋ช ์์ ํฌ์ปค์ค ์ด๋ + case types.FOCUS_PANEL: { + console.log('[panelReducer] ๐ฏ FOCUS_PANEL', { + panelName: action.payload.panelName, + focusTarget: action.payload.focusTarget, + timestamp: action.payload.timestamp, + }); + + return { + ...state, + lastFocusTarget: { + panelName: action.payload.panelName, + focusTarget: action.payload.focusTarget, + timestamp: action.payload.timestamp, + }, + }; + } + default: return state; } diff --git a/com.twin.app.shoptime/src/utils/SpotlightIds.js b/com.twin.app.shoptime/src/utils/SpotlightIds.js index da7fcd6e..0d45a646 100644 --- a/com.twin.app.shoptime/src/utils/SpotlightIds.js +++ b/com.twin.app.shoptime/src/utils/SpotlightIds.js @@ -1,59 +1,60 @@ export const SpotlightIds = { - TPANEL: "tpanel", - TBODY: "tbody", - TPOPUP: "tpopup", - HOME_TBODY: "home_tbody", - TITEM_CARD: "tItemCard", - TAB_LAYOUT: "tablayout", + TPANEL: 'tpanel', + TBODY: 'tbody', + TPOPUP: 'tpopup', + HOME_TBODY: 'home_tbody', + TITEM_CARD: 'tItemCard', + TAB_LAYOUT: 'tablayout', // homePanel - HOME_CATEGORY_NAV: "homeCategoryNav", + HOME_CATEGORY_NAV: 'homeCategoryNav', // FeaturedBrandsPanel - BRAND_VERTICAL_PAGENATOR: "brandVerticalPagenator", - BRAND_QUICK_MENU: "brandQuickMenu", - BRAND_TOP_BUTTON: "brandTopButton", + BRAND_VERTICAL_PAGENATOR: 'brandVerticalPagenator', + BRAND_QUICK_MENU: 'brandQuickMenu', + BRAND_TOP_BUTTON: 'brandTopButton', // TrendingNowPanel - TRENDING_NOW_VERTICAL_PAGINATOR: "trendingNowVerticalPaginator", - TRENDING_NOW_POPULAR_SHOW: "trendingNowPopularShow", - TRENDING_NOW_POPULAR_VIDEO: "trendingNowPopularVideo", - TRENDING_NOW_POPULAR_GRID_LIST: "trendingNowVirtualGridList", - TRENDING_NOW_PREV_INDICATOR: "trendingNowPrevIndicator", - TRENDING_NOW_NEXT_INDICATOR: "trendingNowNextIndicator", - TRENDING_NOW_BEST_SELLER: "trendingNowBestSeller", - TRENDING_NOW_TOP_BUTTON: "trendingNowTopButton", + TRENDING_NOW_VERTICAL_PAGINATOR: 'trendingNowVerticalPaginator', + TRENDING_NOW_POPULAR_SHOW: 'trendingNowPopularShow', + TRENDING_NOW_POPULAR_VIDEO: 'trendingNowPopularVideo', + TRENDING_NOW_POPULAR_GRID_LIST: 'trendingNowVirtualGridList', + TRENDING_NOW_PREV_INDICATOR: 'trendingNowPrevIndicator', + TRENDING_NOW_NEXT_INDICATOR: 'trendingNowNextIndicator', + TRENDING_NOW_BEST_SELLER: 'trendingNowBestSeller', + TRENDING_NOW_TOP_BUTTON: 'trendingNowTopButton', // myPagePanel - MY_PAGE_FAVORITES_BOX: "myPageFavoritesBox", - MY_PAGE_REMINDRES_BOX: "myPageRemindresBox", - MY_PAGE_MY_ORDER_BOX: "myPageMyOrderBox", - MY_PAGE_MY_ORDER_TAB_CONTAINER: "myPageMyOrderTabContainer", + MY_PAGE_FAVORITES_BOX: 'myPageFavoritesBox', + MY_PAGE_REMINDRES_BOX: 'myPageRemindresBox', + MY_PAGE_MY_ORDER_BOX: 'myPageMyOrderBox', + MY_PAGE_MY_ORDER_TAB_CONTAINER: 'myPageMyOrderTabContainer', // categoryPanel - CATEGORY_CONTENTS_BOX: "categoryContentsBox", - CATEGORY_TAB_CONTAINER: "categorytabContainer", - SHOW_PRODUCTS_BOX: "showProductsBox", - SHOW_CONTENTS_BOX: "showContentsBox", + CATEGORY_CONTENTS_BOX: 'categoryContentsBox', + CATEGORY_TAB_CONTAINER: 'categorytabContainer', + SHOW_PRODUCTS_BOX: 'showProductsBox', + SHOW_CONTENTS_BOX: 'showContentsBox', // video player - PLAYER_SKIPINTRO: "skipintro", - PLAYER_TITLE_LAYER: "playerTitleLayer", - PLAYER_SLIDER: "playerslider", - PLAYER_TAB_BUTTON: "playerTabArrow", - PLAYER_BACK_BUTTON: "player-back-button", - PLAYER_SUBTITLE_BUTTON: "player-subtitlebutton", + PLAYER_SKIPINTRO: 'skipintro', + PLAYER_TITLE_LAYER: 'playerTitleLayer', + PLAYER_SLIDER: 'playerslider', + PLAYER_TAB_BUTTON: 'playerTabArrow', + PLAYER_BACK_BUTTON: 'player-back-button', + PLAYER_SUBTITLE_BUTTON: 'player-subtitlebutton', // searchPanel - SEARCH_THEME: "search_theme", - SEARCH_SHOW: "search_show", - SEARCH_ITEM: "search_item", - SEARCH_BESTSELLER: "search_bestseller", - SEARCH_TAB_CONTAINER: "searchtabContainer", + SEARCH_THEME: 'search_theme', + SEARCH_SHOW: 'search_show', + SEARCH_ITEM: 'search_item', + SEARCH_BESTSELLER: 'search_bestseller', + SEARCH_TAB_CONTAINER: 'searchtabContainer', // pin Code Popup - PINCODE_CONTAINER: "pincodeContainer", + PINCODE_CONTAINER: 'pincodeContainer', // detailPanel - DETAIL_BUYNOW: "detail_buynow", - DETAIL_SHOPBYMOBILE: "detail_shop_by_mobile", + DETAIL_BUYNOW: 'detail_buynow', + DETAIL_SHOPBYMOBILE: 'detail_shop_by_mobile', + DETAIL_PRODUCTVIDEO: 'product-video-player', }; diff --git a/com.twin.app.shoptime/src/utils/focusPanelGuide.js b/com.twin.app.shoptime/src/utils/focusPanelGuide.js new file mode 100644 index 00000000..235fd833 --- /dev/null +++ b/com.twin.app.shoptime/src/utils/focusPanelGuide.js @@ -0,0 +1,286 @@ +/** + * [251114] focusPanel ์ก์ ์ฌ์ฉ ๊ฐ์ด๋ + * + * Panel์ ๋น๋๊ธฐ ์์ (useEffect, ํ์ด๋จธ ๋ฑ)์ด ํฌ์ปค์ค๋ฅผ ํ์ทจํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ + * ๋ช ์์ ํฌ์ปค์ค ์ ์ด ์์คํ ์ ๋๋ค. + * + * ๋ฌธ์ ์ํฉ: + * - updatePanel ํธ์ถ โ Panel์ useEffect ์์ + * - pushPanel ํธ์ถ โ ์ Panel์ ํฌ์ปค์ค ์ค์ + * - Panel์ useEffect ์๋ฃ โ ๊ธฐ์กด Panel์ด ํฌ์ปค์ค ํ์ทจ (์ํ์ง ์๋ ๋์) + * + * ํด๊ฒฐ์ฑ : + * - updatePanel, pushPanel ๋ฑ์ผ๋ก Panel์ ์ ์ดํ ๋ + * - focusPanel()๋ก ๋ช ์์ ์ผ๋ก ํฌ์ปค์ค ๋์์ ์ง์ + * - Panel์ useEffect๋ ๋ ์ด์ ์๋ ํฌ์ปค์ค ์ด๋ ์ ํจ + */ + +import { focusPanel } from '../actions/panelActions'; +import { panel_names } from './Config'; + +// ============================================================================ +// ์์ 1: ๋จ์ผ Panel ํฌ์ปค์ค ์ ์ด (๊ฐ์ฅ ๊ฐ๋จ) +// ============================================================================ + +export const example1_singlePanelFocus = (dispatch) => { + console.log('โ ์์ 1: ๋จ์ผ Panel ํฌ์ปค์ค ์ ์ด'); + + // DETAIL_PANEL ํ์ + ํฌ์ปค์ค ์ค์ + dispatch(pushPanel({ name: panel_names.DETAIL_PANEL })); + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-buy-button')); +}; + +// ============================================================================ +// ์์ 2: 2์ค ๊ตฌ์กฐ (PLAYER + DETAIL)์์ ํฌ์ปค์ค ์ ์ด +// ============================================================================ + +export const example2_twoLayerFocus = (dispatch) => { + console.log('โ ์์ 2: 2์ค ๊ตฌ์กฐ ํฌ์ปค์ค ์ ์ด'); + + // ์ํฉ: [PLAYER_PANEL]์ด ์ด๋ฏธ ์๋ ์ํ + + // 1๋จ๊ณ: DETAIL_PANEL ์ถ๊ฐ + dispatch(pushPanel({ name: panel_names.DETAIL_PANEL })); + + // 2๋จ๊ณ: DETAIL_PANEL์ ํน์ ์์์ ํฌ์ปค์ค + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-buy-button')); + + // ๋๋ PLAYER_PANEL์ ํ๋ ์ด๋ฒํผ์ ํฌ์ปค์ค + // dispatch(focusPanel(panel_names.PLAYER_PANEL, 'player-play-button')); +}; + +// ============================================================================ +// ์์ 3: 3์ค ๊ตฌ์กฐ (PLAYER + DETAIL + MEDIA)์์ ํฌ์ปค์ค ์ ์ด +// ============================================================================ + +export const example3_threeLayerFocus = (dispatch) => { + console.log('โ ์์ 3: 3์ค ๊ตฌ์กฐ ํฌ์ปค์ค ์ ์ด'); + + // ์ํฉ: [PLAYER_PANEL, DETAIL_PANEL]์ด ์ด๋ฏธ ์๋ ์ํ + + // 1๋จ๊ณ: MEDIA_PANEL ์ถ๊ฐ (modal=true) + dispatch( + pushPanel({ + name: panel_names.MEDIA_PANEL, + panelInfo: { modal: true }, + }) + ); + + // 2๋จ๊ณ: MEDIA_PANEL์ ๋ซ๊ธฐ ๋ฒํผ์ ํฌ์ปค์ค + dispatch(focusPanel(panel_names.MEDIA_PANEL, 'media-close-button')); + + // ๊ฒฐ๊ณผ: + // - PLAYER_PANEL (๋ฐฑ๊ทธ๋ผ์ด๋) + // - DETAIL_PANEL (์ค๊ฐ, ๋ณด์) + // - MEDIA_PANEL (๋งจ ์, ๋ชจ๋ฌ, ํฌ์ปค์ค ๋ฐ์) +}; + +// ============================================================================ +// ์์ 4: updatePanel๊ณผ ํจ๊ป ์ฌ์ฉ +// ============================================================================ + +import { updatePanel } from '../actions/panelActions'; + +export const example4_updateWithFocus = (dispatch) => { + console.log('โ ์์ 4: updatePanel๊ณผ focusPanel ํจ๊ป ์ฌ์ฉ'); + + // โ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ ๋ฐฉ์: + // dispatch(updatePanel({ + // name: 'DETAIL_PANEL', + // panelInfo: { productId: '123', isLoading: false } + // })); + // DETAIL_PANEL์ useEffect๊ฐ ๋์ค์ ํฌ์ปค์ค๋ฅผ ํ์ทจํ ์ ์์ + + // โ ์ฌ๋ฐ๋ฅธ ๋ฐฉ์: + dispatch( + updatePanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { productId: '123', isLoading: false }, + }) + ); + + // ๋ช ์์ ์ผ๋ก ํฌ์ปค์ค ์ง์ + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-product-title')); +}; + +// ============================================================================ +// ์์ 5: API ํธ์ถ ํ Panel ์ ์ด ๋ฐ ํฌ์ปค์ค +// ============================================================================ + +export const example5_apiAndFocus = (dispatch, getState) => { + console.log('โ ์์ 5: API ํธ์ถ ํ Panel ์ ์ด ๋ฐ ํฌ์ปค์ค'); + + // API ํธ์ถ ์๋ฎฌ๋ ์ด์ + fetch('/api/products/123') + .then((res) => res.json()) + .then((data) => { + // API ์ฑ๊ณต โ Panel ์ ๋ฐ์ดํธ + dispatch( + updatePanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { + product: data, + isLoading: false, + }, + }) + ); + + // ๋ช ์์ ์ผ๋ก ํฌ์ปค์ค ์ง์ (DETAIL_PANEL์ useEffect๋ณด๋ค ๋จผ์ ์คํ๋์ง๋ง + // focusPanel์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌ๋๋ฏ๋ก ๋จผ์ ๋๋๋๋ผ๋ ์์ ) + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-add-to-cart-button')); + }) + .catch((error) => { + // API ์คํจ โ ์๋ฌ Panel ํ์ + dispatch( + pushPanel({ + name: panel_names.ERROR_PANEL, + panelInfo: { error: error.message }, + }) + ); + + // ์๋ฌ Panel์ ํ์ธ ๋ฒํผ์ ํฌ์ปค์ค + dispatch(focusPanel(panel_names.ERROR_PANEL, 'error-ok-button')); + }); +}; + +// ============================================================================ +// ์์ 6: Queue์ ํจ๊ป ์ฌ์ฉ (์์ ๋ณด์ฅ) +// ============================================================================ + +import { pushPanelQueued, updatePanelQueued } from '../actions/queuedPanelActions'; + +export const example6_queueWithFocus = (dispatch) => { + console.log('โ ์์ 6: Panel Queue์ focusPanel ํจ๊ป ์ฌ์ฉ'); + + // Panel ์ ์ด๋ Queue๋ก ์์ ๋ณด์ฅ + dispatch( + updatePanelQueued({ + name: panel_names.DETAIL_PANEL, + panelInfo: { productId: '456', isLoading: false }, + }) + ); + + dispatch( + pushPanelQueued({ + name: panel_names.MEDIA_PANEL, + panelInfo: { modal: true }, + }) + ); + + // ํฌ์ปค์ค๋ ๋ช ์์ ์ผ๋ก ์ง์ + dispatch(focusPanel(panel_names.MEDIA_PANEL, 'media-close-button')); +}; + +// ============================================================================ +// ์์ 7: Panel์์ focusPanel ํธ์ถ +// ============================================================================ + +/* +// DetailPanel.jsx์์: + +import { useDispatch } from 'react-redux'; +import { focusPanel } from '../actions/panelActions'; +import { panel_names } from '../utils/Config'; + +function DetailPanel({ panelInfo }) { + const dispatch = useDispatch(); + + const handleProductSelect = (productId) => { + // ์ ํํ ์ํ์ผ๋ก Panel ์ ๋ฐ์ดํธ + dispatch(updatePanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { productId } + })); + + // ํน์ ์์์ ํฌ์ปค์ค (Panel์ useEffect๊ฐ ํฌ์ปค์ค๋ฅผ ํ์ทจํ์ง ์์) + dispatch(focusPanel( + panel_names.DETAIL_PANEL, + 'detail-product-description' + )); + }; + + return ( +