[FeaturedBrandsPanel] API 구조 변경에 따른 panel 작업

Detail Notes :

1. featuredBrandsSlice.js → brandSlice.js 이름 변경
2. FeaturedBrandsPanel.jsx, QuickMenu.jsx, Banner.jsx 구조 변경 및 css 수정
3. 사용하지 않는 api/featuredBrands.js 파일 삭제
This commit is contained in:
younghoon100.park
2024-01-26 11:15:52 +09:00
parent 8110067e29
commit eadb601b56
9 changed files with 179 additions and 262 deletions

View File

@@ -4,13 +4,17 @@ import { useDispatch } from "react-redux";
import ThemeDecorator from "@enact/sandstone/ThemeDecorator"; import ThemeDecorator from "@enact/sandstone/ThemeDecorator";
import { getBrandList } from "../features/brand/featuredBrandsSlice"; import {
getBrandLayoutInfo,
getBrandList,
getBrandLiveChannelInfo,
} from "../features/brand/brandsSlice";
import { getAuthenticationCode } from "../features/device/deviceSlice"; import { getAuthenticationCode } from "../features/device/deviceSlice";
import { getHomeMenu } from "../features/home/homeSlice"; import { getHomeMenu } from "../features/home/homeSlice";
import { getMyRecommandedKeyword } from "../features/mypage/myPageSlice";
import { getOnSaleInfo } from "../features/onSale/onSaleSlice"; import { getOnSaleInfo } from "../features/onSale/onSaleSlice";
import MainView from "../views/MainView/MainView"; import MainView from "../views/MainView/MainView";
import css from "./App.module.less"; import css from "./App.module.less";
import { getMyRecommandedKeyword } from "../features/mypage/myPageSlice";
function AppBase(props) { function AppBase(props) {
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -21,6 +25,10 @@ function AppBase(props) {
dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: "" })); dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: "" }));
dispatch(getBrandList()); dispatch(getBrandList());
dispatch(getMyRecommandedKeyword()); dispatch(getMyRecommandedKeyword());
// @@pyh Todo, patnrId를 GNB에서 받는 로직이 만들어진 뒤 FeaturedBrandsPanel로 이동
dispatch(getBrandLayoutInfo({ patnrId: "1" }));
dispatch(getBrandLiveChannelInfo({ patnrId: "1" }));
}, [dispatch]); }, [dispatch]);
return <MainView />; return <MainView />;

View File

@@ -1,102 +0,0 @@
/* ----------------------
삭제 예정
---------------------- */
import { URLS } from "./apiConfig";
import api from "./axiosConfig";
// Featured Brands 정보 조회 IF-LGSP-304
export async function getBrandList() {
try {
const response = await api.get(URLS.GET_BRAND_LIST);
return response.data.data;
} catch (error) {
const { response } = error;
if (response) {
const statusCode = response.status;
const statusText = response.statusText;
console.error(`${statusCode} Error, ${statusText}`);
}
// throw new Error(error);
}
}
// Featured Brands LAYOUT (shelf) 정보 조회 IF-LGSP-305
export async function getBrandLayoutInfo(props) {
const { patnrId } = props;
try {
const response = await api.get(URLS.GET_BRAND_LAYOUT_INFO, {
params: {
patnrId,
},
});
return response.data.data;
} catch (error) {
const { response } = error;
if (response) {
const statusCode = response.status;
const statusText = response.statusText;
console.error(`${statusCode} Error, ${statusText}`);
}
// throw new Error(error);
}
}
// Featured Brands Live 채널 정보 조회 IF-LGSP-306
export async function getBrandLiveChannelInfo(props) {
const { patnrId } = props;
try {
const response = await api.get(URLS.GET_BRAND_LIVE_CHANNEL_INFO, {
params: {
patnrId,
},
});
return response.data.data;
} catch (error) {
const { response } = error;
if (response) {
const statusCode = response.status;
const statusText = response.statusText;
console.error(`${statusCode} Error, ${statusText}`);
}
// throw new Error(error);
}
}
// Featured Brands Today's deals 정보 조회 IF-LGSP-307
export async function getBrandTSVInfo(props) {
const { patnrId } = props;
try {
const response = await api.get(URLS.GET_BRAND_TODAYS_DEALS, {
params: {
patnrId,
},
});
return response.data.data;
} catch (error) {
const { response } = error;
if (response) {
const statusCode = response.status;
const statusText = response.statusText;
console.error(`${statusCode} Error, ${statusText}`);
}
// throw new Error(error);
}
}

View File

@@ -0,0 +1,122 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { URLS } from "../../api/apiConfig";
import { TAxios } from "../../api/TAxios";
// Featured Brands 정보 조회 IF-LGSP-304
// @@pyh Todo, 기존의 key가 brandList에서 brandInfo로 변경 됨에 따라 함수명 또한 바뀔 수 있음 (문서 확인 후 변경 처리)
export const getBrandList = createAsyncThunk(
"brand/getBrandList",
async (_, thunkAPI) => {
const onSuccess = (response) => {
console.log("@@ getBrandList onSuccess ", response.data);
thunkAPI.dispatch(brandSlice.actions.updateBrandInfo(response.data.data));
};
const onFail = (error) => {
console.log("@@ getBrandList onFail", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_BRAND_LIST,
{},
{},
onSuccess,
onFail
);
}
);
// Featured Brands LAYOUT (shelf) 정보 조회 IF-LGSP-305
export const getBrandLayoutInfo = createAsyncThunk(
"brand/getBrandLayoutInfo",
async (props, thunkAPI) => {
const { patnrId } = props;
const onSuccess = (response) => {
console.log("@@ getBrandLayoutInfo onSuccess ", response.data);
thunkAPI.dispatch(brandSlice.actions.updateLayoutBrandInfo(response.data.data));
};
const onFail = (error) => {
console.log("@@ getBrandLayoutInfo onFail ", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_BRAND_LAYOUT_INFO,
{ patnrId },
{},
onSuccess,
onFail
);
}
);
// Featured Brands Live 채널 정보 조회 IF-LGSP-306
export const getBrandLiveChannelInfo = createAsyncThunk(
"brand/getBrandLiveChannelInfo",
async (props, thunkAPI) => {
const { patnrId } = props;
const onSuccess = (response) => {
console.log("@@ getBrandLiveChannelInfo onSuccess ", response.data);
thunkAPI.dispatch(brandSlice.actions.updateBrandLiveChannelInfo(response.data.data));
};
const onFail = (error) => {
console.log("@@ getBrandLiveChannelInfo onFail ", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_BRAND_LIVE_CHANNEL_INFO,
{ patnrId },
{},
onSuccess,
onFail
);
}
);
const initialState = {
brandInfoData: {},
brandLayoutInfoData: {},
brandLiveChannelInfoData: {},
};
export const brandSlice = createSlice({
name: "brand",
initialState,
reducers: {
updateBrandInfo: (state, action) => {
state.brandInfoData = action.payload;
},
updateLayoutBrandInfo: (state, action) => {
state.brandLayoutInfoData = action.payload;
},
updateBrandLiveChannelInfo: (state, action) => {
state.brandLiveChannelInfoData = action.payload;
},
},
});
export const { updateBrandInfo, updateLayoutBrandInfo, updateBrandLiveChannelInfo } =
brandSlice.actions;
export default brandSlice.reducer;

View File

@@ -1,51 +0,0 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { URLS } from "../../api/apiConfig";
import { TAxios } from "../../api/TAxios";
// Featured Brands 정보 조회 IF-LGSP-304
// @@pyh Todo, 기존의 key가 brandList에서 brandInfo로 변경 됨에 따라 함수명 또한 바뀔 수 있음 (문서 확인 후 변경 처리)
export const getBrandList = createAsyncThunk(
"featuredBrands/getBrandList",
async (_, thunkAPI) => {
const onSuccess = (response) => {
console.log("getBrandList onSuccess ", response.data);
thunkAPI.dispatch(featuredBrandsSlice.actions.updateBrandInfo(response.data.data));
};
const onFail = (error) => {
console.log("getBrandList onFail", error);
};
TAxios(
thunkAPI.dispatch,
thunkAPI.getState,
"get",
URLS.GET_BRAND_LIST,
{},
{},
onSuccess,
onFail
);
}
);
const initialState = {
brandInfo: {},
};
export const featuredBrandsSlice = createSlice({
name: "featuredBrands",
initialState,
reducers: {
updateBrandInfo: (state, action) => {
state.featuredBrandsData = action.payload;
},
},
});
export const { updateBrandInfo } = featuredBrandsSlice.actions;
export default featuredBrandsSlice.reducer;

View File

@@ -1,13 +1,13 @@
import { configureStore } from "@reduxjs/toolkit"; import { configureStore } from "@reduxjs/toolkit";
import appDataReducer from "../features/appData/appDataSlice"; import appDataReducer from "../features/appData/appDataSlice";
import featuredBrandsReducer from "../features/brand/featuredBrandsSlice"; import brandReducer from "../features/brand/brandsSlice";
import commonReducer from "../features/common/commonSlice"; import commonReducer from "../features/common/commonSlice";
import deviceReducer from "../features/device/deviceSlice"; import deviceReducer from "../features/device/deviceSlice";
import homeReducer from "../features/home/homeSlice"; import homeReducer from "../features/home/homeSlice";
import myPageReducer from "../features/mypage/myPageSlice";
import onSaleReducer from "../features/onSale/onSaleSlice"; import onSaleReducer from "../features/onSale/onSaleSlice";
import panelsReducer from "../features/panels/panelsSlice"; import panelsReducer from "../features/panels/panelsSlice";
import myPageReducer from "../features/mypage/myPageSlice";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
@@ -17,7 +17,7 @@ export const store = configureStore({
common: commonReducer, common: commonReducer,
home: homeReducer, home: homeReducer,
onSale: onSaleReducer, onSale: onSaleReducer,
featuredBrands: featuredBrandsReducer, brand: brandReducer,
myPage: myPageReducer, myPage: myPageReducer,
}, },
}); });

View File

@@ -2,14 +2,14 @@ import React from "react";
import css from "./Banner.module.less"; import css from "./Banner.module.less";
export default function Banner({ selectedBrandInfo, topImgInfo }) { export default function Banner({ selectedBrandInfo, brandTopImgInfo }) {
const { logoImgAlt, logoImgPath, patncNm } = selectedBrandInfo; const { logoImgAlt, logoImgPath, patncNm } = selectedBrandInfo;
const { topImgAlt, topImgNm, topImgPath } = topImgInfo; const { topImgAlt, topImgPath } = brandTopImgInfo;
return ( return (
<div className={css.container}> <div className={css.container}>
<div> <div>
<img src={logoImgPath} alt={`${logoImgAlt} ${topImgNm}`} /> <img src={logoImgPath} alt={logoImgAlt} />
<h2>{patncNm}</h2> <h2>{patncNm}</h2>
</div> </div>
<img src={topImgPath} alt={topImgAlt} /> <img src={topImgPath} alt={topImgAlt} />

View File

@@ -10,7 +10,7 @@
.flex(); .flex();
gap: 12px; gap: 12px;
// @@ Todo, padding-left 전체적으로 적용유무 수정 // @@pyh Todo, padding-left 전체적으로 적용유무 수정
padding-left: 60px; padding-left: 60px;
h2 { h2 {
@@ -24,7 +24,7 @@
} }
> img { > img {
.position(@position: absolute); .position(@position: absolute, @top: 0, @right: 0);
width: 100%; width: 100%;
height: 438px; height: 438px;
object-fit: cover; object-fit: cover;

View File

@@ -1,107 +1,54 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { import { useDispatch, useSelector } from "react-redux";
getBrandLayoutInfo,
getBrandList,
getBrandLiveChannelInfo,
} from "../../api/featuredBrands";
import TPanel from "../../components/TPanel/TPanel"; import TPanel from "../../components/TPanel/TPanel";
import { getBrandLayoutInfo } from "../../features/brand/brandsSlice";
import Banner from "./Banner/Banner"; import Banner from "./Banner/Banner";
import css from "./FeaturedBrandsPanel.module.less"; import css from "./FeaturedBrandsPanel.module.less";
import QuickMenu from "./QuickMenu/QuickMenu"; import QuickMenu from "./QuickMenu/QuickMenu";
/* const getSelectedBrandInfo = (brandInfo, selectedPatnrId) => {
return brandInfo.find((brand) => brand?.patnrId === selectedPatnrId);
key, value and dataType of BrandList };
expsOrd: 1 / number
logoImgAlt: "qvc" / string
logoImgNm: "us_qvc.png" / string
logoImgPath: "http://qt2-ngfts.lge.com/fts/gftsDownload.lge?biz_code=LGSHOPPING&func_code=IMAGE&file_path=/lgshopping/image/us_qvc.png" / string
newFlag: "N" / string
patncLogoPath: "http://qt2-ngfts.lge.com/fts/gftsDownload.lge?biz_code=LGSHOPPING&func_code=IMAGE&file_path=/lgshopping/image/small_logo_qvc.png" / string
patncNm: "QVC" / string
patnrId: "1" / string
*/
export default function FeaturedBrandsPanel() { export default function FeaturedBrandsPanel() {
// @@ Todo, provided by GNB as props or global state const dispatch = useDispatch();
const [brandList, setBrandList] = useState([]);
const brandInfo = useSelector((state) => state.brand.brandInfoData.brandInfo);
const brandTopImgInfo = useSelector((state) => state.brand.brandLayoutInfoData.brandTopImgInfo);
const [selectedBrandInfo, setSelectedBrandInfo] = useState();
// @@ Todo, provided by GNB as props or global state // @@ Todo, provided by GNB as props or global state
const [selectedBrandId, setSelectedBrandId] = useState("1"); const [selectedPatnrId, setSelectedPatnrId] = useState("1");
const [selectedBrandInfo, setSelectedBrandInfo] = useState({});
const [topImgInfo, setTopImgInfo] = useState({});
const handleQuickMenu = (patnrId) => { const handleQuickMenu = (patnrId) => {
const brandInfo = brandList.find((brand) => brand.patnrId === patnrId); setSelectedPatnrId(patnrId);
setSelectedBrandInfo(brandInfo);
setSelectedBrandId(patnrId);
};
const fetchBrandList = async () => {
try {
const data = await getBrandList();
console.log("Brand List:", data);
if (data) {
setBrandList(data.brandList);
setSelectedBrandInfo(() =>
data.brandList.find(({ patnrId }) => patnrId === selectedBrandId)
);
}
} catch (error) {
console.error("Error fetching Brand List:", error);
}
};
const fetchBrandLayoutInfo = async (patnrId) => {
try {
const data = await getBrandLayoutInfo({ patnrId });
console.log("Brand Layout Info:", data);
if (data) {
setTopImgInfo(data.topImgInfo);
}
} catch (error) {
console.error("Error fetching Brand Layout Info:", error);
}
};
const fetchBrandLiveChannelInfo = async (patnrId) => {
try {
const data = await getBrandLiveChannelInfo({ patnrId });
console.log("Brand Live Channel Info:", data);
if (data) {
}
} catch (error) {
console.error("Error fetching Brand Live Channel Info:", error);
}
}; };
useEffect(() => { useEffect(() => {
// @@ Todo, provided by GNB as props or global state if (brandInfo) {
setSelectedBrandId("1"); setSelectedBrandInfo(getSelectedBrandInfo(brandInfo, selectedPatnrId));
fetchBrandList(); }
}, []);
useEffect(() => { dispatch(getBrandLayoutInfo({ patnrId: selectedPatnrId }));
fetchBrandLayoutInfo(selectedBrandId); }, [selectedPatnrId]);
fetchBrandLiveChannelInfo(selectedBrandId);
}, [selectedBrandId]);
return ( return (
/* scenario page 98 */ /* scenario page 98 */
<TPanel className={css.container}> <TPanel className={css.container}>
{brandList && brandList.length > 1 && ( {brandInfo && brandInfo.length > 1 && (
<QuickMenu <QuickMenu
brandList={brandList} brandInfo={brandInfo}
onQuickMenuClick={handleQuickMenu} onQuickMenuClick={handleQuickMenu}
selectedBrandId={selectedBrandId} selectedPatnrId={selectedPatnrId}
/> />
)} )}
<Banner selectedBrandInfo={selectedBrandInfo} topImgInfo={topImgInfo} /> {selectedBrandInfo && (
<Banner selectedBrandInfo={selectedBrandInfo} brandTopImgInfo={brandTopImgInfo} />
)}
</TPanel> </TPanel>
); );
} }

View File

@@ -1,12 +1,11 @@
import React, { useEffect } from 'react'; import React, { useEffect } from "react";
import Spotlight from '@enact/spotlight'; import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
from '@enact/spotlight/SpotlightContainerDecorator'; import Spottable from "@enact/spotlight/Spottable";
import Spottable from '@enact/spotlight/Spottable';
import { $L } from '../../../utils/helperMethods'; import { $L } from "../../../utils/helperMethods";
import css from './QuickMenu.module.less'; import css from "./QuickMenu.module.less";
const Container = SpotlightContainerDecorator( const Container = SpotlightContainerDecorator(
{ leaveFor: { right: "" }, enterTo: "last-focused" }, { leaveFor: { right: "" }, enterTo: "last-focused" },
@@ -15,14 +14,9 @@ const Container = SpotlightContainerDecorator(
const SpottableComponent = Spottable("li"); const SpottableComponent = Spottable("li");
export default function QuickMenu({ export default function QuickMenu({ brandInfo, selectedPatnrId, onQuickMenuClick, ...rest }) {
brandList,
selectedBrandId,
onQuickMenuClick,
...rest
}) {
const handleClick = (patnrId) => { const handleClick = (patnrId) => {
if (selectedBrandId === patnrId) { if (selectedPatnrId === patnrId) {
return; return;
} }
@@ -30,25 +24,24 @@ export default function QuickMenu({
}; };
useEffect(() => { useEffect(() => {
const element = document.getElementById(selectedBrandId); const element = document.getElementById(selectedPatnrId);
Spotlight.focus(element); Spotlight.focus(element);
}, []); }, []);
return ( return (
<Container {...rest} className={css.container}> <Container {...rest} className={css.container}>
<ul> <ul>
{brandList && {brandInfo &&
brandList.map(({ logoImgAlt, logoImgPath, newFlag, patnrId }) => { brandInfo.map(({ logoImgAlt, logoImgPath, newFlag, patnrId }) => {
return ( return (
<SpottableComponent <SpottableComponent
className={patnrId === selectedBrandId && css.selected} className={patnrId === selectedPatnrId && css.selected}
id={patnrId} id={patnrId}
key={patnrId} key={patnrId}
onClick={() => handleClick(patnrId)} onClick={() => handleClick(patnrId)}
> >
{newFlag === "Y" && ( {newFlag === "Y" && <span className={css.newBagde}>{$L("NEW")}</span>}
<span className={css.newBagde}>{$L("NEW")}</span>
)}
<span className={css.outline} /> <span className={css.outline} />
<img src={logoImgPath} alt={logoImgAlt} /> <img src={logoImgPath} alt={logoImgAlt} />
</SpottableComponent> </SpottableComponent>