[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:
@@ -4,13 +4,17 @@ import { useDispatch } from "react-redux";
|
||||
|
||||
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 { getHomeMenu } from "../features/home/homeSlice";
|
||||
import { getMyRecommandedKeyword } from "../features/mypage/myPageSlice";
|
||||
import { getOnSaleInfo } from "../features/onSale/onSaleSlice";
|
||||
import MainView from "../views/MainView/MainView";
|
||||
import css from "./App.module.less";
|
||||
import { getMyRecommandedKeyword } from "../features/mypage/myPageSlice";
|
||||
|
||||
function AppBase(props) {
|
||||
const dispatch = useDispatch();
|
||||
@@ -21,6 +25,10 @@ function AppBase(props) {
|
||||
dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: "" }));
|
||||
dispatch(getBrandList());
|
||||
dispatch(getMyRecommandedKeyword());
|
||||
|
||||
// @@pyh Todo, patnrId를 GNB에서 받는 로직이 만들어진 뒤 FeaturedBrandsPanel로 이동
|
||||
dispatch(getBrandLayoutInfo({ patnrId: "1" }));
|
||||
dispatch(getBrandLiveChannelInfo({ patnrId: "1" }));
|
||||
}, [dispatch]);
|
||||
|
||||
return <MainView />;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
122
com.twin.app.shoptime/src/features/brand/brandsSlice.js
Normal file
122
com.twin.app.shoptime/src/features/brand/brandsSlice.js
Normal 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;
|
||||
@@ -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;
|
||||
@@ -1,13 +1,13 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
|
||||
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 deviceReducer from "../features/device/deviceSlice";
|
||||
import homeReducer from "../features/home/homeSlice";
|
||||
import myPageReducer from "../features/mypage/myPageSlice";
|
||||
import onSaleReducer from "../features/onSale/onSaleSlice";
|
||||
import panelsReducer from "../features/panels/panelsSlice";
|
||||
import myPageReducer from "../features/mypage/myPageSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@@ -17,7 +17,7 @@ export const store = configureStore({
|
||||
common: commonReducer,
|
||||
home: homeReducer,
|
||||
onSale: onSaleReducer,
|
||||
featuredBrands: featuredBrandsReducer,
|
||||
brand: brandReducer,
|
||||
myPage: myPageReducer,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,14 +2,14 @@ import React from "react";
|
||||
|
||||
import css from "./Banner.module.less";
|
||||
|
||||
export default function Banner({ selectedBrandInfo, topImgInfo }) {
|
||||
export default function Banner({ selectedBrandInfo, brandTopImgInfo }) {
|
||||
const { logoImgAlt, logoImgPath, patncNm } = selectedBrandInfo;
|
||||
const { topImgAlt, topImgNm, topImgPath } = topImgInfo;
|
||||
const { topImgAlt, topImgPath } = brandTopImgInfo;
|
||||
|
||||
return (
|
||||
<div className={css.container}>
|
||||
<div>
|
||||
<img src={logoImgPath} alt={`${logoImgAlt} ${topImgNm}`} />
|
||||
<img src={logoImgPath} alt={logoImgAlt} />
|
||||
<h2>{patncNm}</h2>
|
||||
</div>
|
||||
<img src={topImgPath} alt={topImgAlt} />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.flex();
|
||||
gap: 12px;
|
||||
|
||||
// @@ Todo, padding-left 전체적으로 적용유무 수정
|
||||
// @@pyh Todo, padding-left 전체적으로 적용유무 수정
|
||||
padding-left: 60px;
|
||||
|
||||
h2 {
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
|
||||
> img {
|
||||
.position(@position: absolute);
|
||||
.position(@position: absolute, @top: 0, @right: 0);
|
||||
width: 100%;
|
||||
height: 438px;
|
||||
object-fit: cover;
|
||||
|
||||
@@ -1,107 +1,54 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
getBrandLayoutInfo,
|
||||
getBrandList,
|
||||
getBrandLiveChannelInfo,
|
||||
} from "../../api/featuredBrands";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import TPanel from "../../components/TPanel/TPanel";
|
||||
import { getBrandLayoutInfo } from "../../features/brand/brandsSlice";
|
||||
import Banner from "./Banner/Banner";
|
||||
import css from "./FeaturedBrandsPanel.module.less";
|
||||
import QuickMenu from "./QuickMenu/QuickMenu";
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
const getSelectedBrandInfo = (brandInfo, selectedPatnrId) => {
|
||||
return brandInfo.find((brand) => brand?.patnrId === selectedPatnrId);
|
||||
};
|
||||
|
||||
export default function FeaturedBrandsPanel() {
|
||||
// @@ Todo, provided by GNB as props or global state
|
||||
const [brandList, setBrandList] = useState([]);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
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
|
||||
const [selectedBrandId, setSelectedBrandId] = useState("1");
|
||||
|
||||
const [selectedBrandInfo, setSelectedBrandInfo] = useState({});
|
||||
const [topImgInfo, setTopImgInfo] = useState({});
|
||||
const [selectedPatnrId, setSelectedPatnrId] = useState("1");
|
||||
|
||||
const handleQuickMenu = (patnrId) => {
|
||||
const brandInfo = brandList.find((brand) => brand.patnrId === 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);
|
||||
}
|
||||
setSelectedPatnrId(patnrId);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// @@ Todo, provided by GNB as props or global state
|
||||
setSelectedBrandId("1");
|
||||
fetchBrandList();
|
||||
}, []);
|
||||
if (brandInfo) {
|
||||
setSelectedBrandInfo(getSelectedBrandInfo(brandInfo, selectedPatnrId));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchBrandLayoutInfo(selectedBrandId);
|
||||
fetchBrandLiveChannelInfo(selectedBrandId);
|
||||
}, [selectedBrandId]);
|
||||
dispatch(getBrandLayoutInfo({ patnrId: selectedPatnrId }));
|
||||
}, [selectedPatnrId]);
|
||||
|
||||
return (
|
||||
/* scenario page 98 */
|
||||
<TPanel className={css.container}>
|
||||
{brandList && brandList.length > 1 && (
|
||||
{brandInfo && brandInfo.length > 1 && (
|
||||
<QuickMenu
|
||||
brandList={brandList}
|
||||
brandInfo={brandInfo}
|
||||
onQuickMenuClick={handleQuickMenu}
|
||||
selectedBrandId={selectedBrandId}
|
||||
selectedPatnrId={selectedPatnrId}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Banner selectedBrandInfo={selectedBrandInfo} topImgInfo={topImgInfo} />
|
||||
{selectedBrandInfo && (
|
||||
<Banner selectedBrandInfo={selectedBrandInfo} brandTopImgInfo={brandTopImgInfo} />
|
||||
)}
|
||||
</TPanel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import SpotlightContainerDecorator
|
||||
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
import Spotlight from "@enact/spotlight";
|
||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||
import Spottable from "@enact/spotlight/Spottable";
|
||||
|
||||
import { $L } from '../../../utils/helperMethods';
|
||||
import css from './QuickMenu.module.less';
|
||||
import { $L } from "../../../utils/helperMethods";
|
||||
import css from "./QuickMenu.module.less";
|
||||
|
||||
const Container = SpotlightContainerDecorator(
|
||||
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||
@@ -15,14 +14,9 @@ const Container = SpotlightContainerDecorator(
|
||||
|
||||
const SpottableComponent = Spottable("li");
|
||||
|
||||
export default function QuickMenu({
|
||||
brandList,
|
||||
selectedBrandId,
|
||||
onQuickMenuClick,
|
||||
...rest
|
||||
}) {
|
||||
export default function QuickMenu({ brandInfo, selectedPatnrId, onQuickMenuClick, ...rest }) {
|
||||
const handleClick = (patnrId) => {
|
||||
if (selectedBrandId === patnrId) {
|
||||
if (selectedPatnrId === patnrId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,25 +24,24 @@ export default function QuickMenu({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const element = document.getElementById(selectedBrandId);
|
||||
const element = document.getElementById(selectedPatnrId);
|
||||
|
||||
Spotlight.focus(element);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container {...rest} className={css.container}>
|
||||
<ul>
|
||||
{brandList &&
|
||||
brandList.map(({ logoImgAlt, logoImgPath, newFlag, patnrId }) => {
|
||||
{brandInfo &&
|
||||
brandInfo.map(({ logoImgAlt, logoImgPath, newFlag, patnrId }) => {
|
||||
return (
|
||||
<SpottableComponent
|
||||
className={patnrId === selectedBrandId && css.selected}
|
||||
className={patnrId === selectedPatnrId && css.selected}
|
||||
id={patnrId}
|
||||
key={patnrId}
|
||||
onClick={() => handleClick(patnrId)}
|
||||
>
|
||||
{newFlag === "Y" && (
|
||||
<span className={css.newBagde}>{$L("NEW")}</span>
|
||||
)}
|
||||
{newFlag === "Y" && <span className={css.newBagde}>{$L("NEW")}</span>}
|
||||
<span className={css.outline} />
|
||||
<img src={logoImgPath} alt={logoImgAlt} />
|
||||
</SpottableComponent>
|
||||
|
||||
Reference in New Issue
Block a user