detail 레이아웃 작업 및 indicator추가 TButton BackKey props 추가

This commit is contained in:
고동영
2024-02-15 09:56:18 +09:00
parent a9c3c5a0ea
commit 64a18c7d77
18 changed files with 419 additions and 14 deletions

View File

@@ -34,6 +34,7 @@ export const types = {
GET_SUB_CATEGORY: "GET_SUB_CATEGORY",
APPEND_SUB_CATEGORY: "APPEND_SUB_CATEGORY",
GET_TOP_20_SHOW: "GET_TOP_20_SHOW",
GET_PRODUCT_DETAIL: "GET_PRODUCT_DETAIL",
// myPage actions
GET_MY_RECOMMANDED_KEYWORD: "GET_MY_RECOMMANDED_KEYWORD",
@@ -44,6 +45,8 @@ export const types = {
// product actions
GET_BEST_SELLER: "GET_BEST_SELLER",
GET_PRODUCT_GROUP: "GET_PRODUCT_GROUP",
GET_PRODUCT_OPTION: "GET_PRODUCT_OPTION",
// search actions
GET_SEARCH: "GET_SEARCH",

View File

@@ -5,6 +5,33 @@ import { types } from "./actionTypes";
// 서브카테고리 조회 IF-LGSP-051
let getSubCategoryKey = null;
export const getMainCategoryDetail = (props) => (dispatch, getState) => {
const { patnrId, prdtId } = props;
const onSuccess = (response) => {
console.log("getMainCategoryDetail onSuccess ", response.data);
dispatch({
type: types.GET_PRODUCT_DETAIL,
payload: response.data.data,
});
};
const onFail = (error) => {
console.error("getMainCategoryDetail onFail", error);
};
TAxios(
dispatch,
getState,
"get",
URLS.GET_PRODUCT_DETAIL,
{ patnrId, prdtId },
{},
onSuccess,
onFail
);
};
export const getSubCategory = (params, key) => (dispatch, getState) => {
const { lgCatCd, patnrIdList, tabType, pageSize, pageNo, filterType } =
params;

View File

@@ -28,3 +28,61 @@ export const getBestSeller = () => (dispatch, getState) => {
onFail
);
};
// Detail 옵션상품 정보 조회 IF-LGSP-319
export const getProductGroup = (props) => (dispatch, getState) => {
const { patnrId, prdtId } = props;
const onSuccess = (response) => {
console.log("getProductGroup onSuccess", response.data);
dispatch({
type: types.GET_PRODUCT_GROUP,
payload: response.data.data,
});
};
const onFail = (error) => {
console.error("getProductGroup onFail", error);
};
TAxios(
dispatch,
getState,
"get",
URLS.GET_PRODUCT_GROUP,
{ patnrId, prdtId },
{},
onSuccess,
onFail
);
};
// Detail 옵션상품 정보 조회 IF-LGSP-320
export const getProductOption = (props) => (dispatch, getState) => {
const { patnrId, prdtId } = props;
const onSuccess = (response) => {
console.log("getProductOption onSuccess", response.data);
dispatch({
type: types.GET_PRODUCT_OPTION,
payload: response.data.data,
});
};
const onFail = (error) => {
console.error("getProductOption onFail", error);
};
TAxios(
dispatch,
getState,
"get",
URLS.GET_PRODUCT_OPTION,
{ patnrId, prdtId },
{},
onSuccess,
onFail
);
};

View File

@@ -34,7 +34,8 @@ export const URLS = {
//product controller
GET_PRODUCT_BESTSELLER: "/lgsp/v1/product/bestSeller.lge",
GET_PRODUCT_GROUP: "/lgsp/v1/product/group.lge",
GET_PRODUCT_OPTION: "/lgsp/v1/product/option.lge",
//my-page controller
GET_MY_RECOMMANDED_KEYWORD: "/lgsp/v1/mypage/reckeyword.lge",
GET_MY_FAQ_INFO: "/lgsp/v1/mypage/support/faq.lge",
@@ -47,12 +48,13 @@ export const URLS = {
//main controller
GET_SUB_CATEGORY: "/lgsp/v1/main/subcategory.lge",
GET_TOP20_SHOW: "/lgsp/v1/main/top/show.lge",
GET_PRODUCT_DETAIL: "/lgsp/v1/main/category/product/detail.lge",
//event controller
GET_WELCOME_EVENT_INFO: "/lgsp/v1/event/event.lge",
};
const getRicCode = (country, ricCodeSetting) =>{
if(ricCodeSetting !== 'system'){
const getRicCode = (country, ricCodeSetting) => {
if (ricCodeSetting !== "system") {
return ricCodeSetting;
}
if (country == "US") {
@@ -63,27 +65,28 @@ const getRicCode = (country, ricCodeSetting) =>{
return "ruc";
}
return null;
}
};
export const getUrl = (getState, endStr) => {
const serverHOST = getState().common.appStatus.serverHOST;
const {serverType, ricCodeSetting} = getState().localSettings;
if(!serverHOST){
console.error('getUrl: Not supported. Host is missing');
const { serverType, ricCodeSetting } = getState().localSettings;
if (!serverHOST) {
console.error("getUrl: Not supported. Host is missing");
return "";
}
let sdpURL = serverHOST.split(".")[0];
let countryCode = "", ricCode="";
let countryCode = "",
ricCode = "";
if (sdpURL.indexOf("-") > 0) {
countryCode = sdpURL.split("-")[1];
} else {
countryCode = sdpURL;
}
ricCode = getRicCode(countryCode, ricCodeSetting);
if(!ricCode){
if (!ricCode) {
return "";
}
sdpURL = sdpURL.toLowerCase();
if(serverType !== 'system'){
if (serverType !== "system") {
sdpURL = serverType;
}
let newUrl = "";

View File

@@ -32,7 +32,7 @@ export default function TBody({
{children}
</TScroller>
) : (
{ children }
children
)}
</Container>
);

View File

@@ -1,17 +1,20 @@
import React from "react";
import css from "./THeader.module.less";
import classNames from "classnames";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import css from "./THeader.module.less";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
export default function THeader({ title, className }) {
export default function THeader({ title, className, onBackButton, onClick }) {
return (
<Container className={classNames(css.tHeader, className)}>
{onBackButton && <button className={css.button} onClick={onClick} />}
<div className={css.title}>{title}</div>
</Container>
);

View File

@@ -15,4 +15,18 @@
font-size: 42px;
margin-left: 61px;
}
}
.button {
.size(@w: 60px, @h: 60px);
background-size: cover;
background-position: center;
background-image: url('../../../assets/button/60x60/btn-60-bk-back-nor@3x.png');
border: none;
margin-right: -49px;
}

View File

@@ -3,6 +3,7 @@ import { types } from "../actions/actionTypes";
const initialState = {
subCategoryData: {},
top20ShowData: {},
productData: {},
};
export const mainReducer = (state = initialState, action) => {
@@ -18,6 +19,11 @@ export const mainReducer = (state = initialState, action) => {
...state,
top20ShowData: action.payload,
};
case types.GET_PRODUCT_DETAIL:
return {
...state,
productData: action.payload.product[0],
};
default:
return state;

View File

@@ -11,7 +11,16 @@ export const productReducer = (state = initialState, action) => {
...state,
bestSellerData: action.payload,
};
case types.GET_PRODUCT_OPTION:
return {
...state,
prdtOptInfo: action.payload.prdtOptInfo,
};
case types.GET_PRODUCT_GROUP:
return {
...state,
groupInfo: action.payload.groupInfo,
};
default:
return state;
}

View File

@@ -19,6 +19,7 @@ export const panel_names = {
CART_PANEL: "cartpanel",
FEATURED_BRANDS_PANEL: "featuredbrandspanel",
WELCOME_EVENT_PANEL: "welcomeeventpanel",
DETAIL_PANEL: "detailpanel",
// error
ERROR_PANEL: "errorpanel",

View File

@@ -0,0 +1,47 @@
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getMainCategoryDetail } from "../../actions/mainActions";
import TBody from "../../components/TBody/TBody";
import THeader from "../../components/THeader/THeader";
import TPanel from "../../components/TPanel/TPanel";
import css from "./ItemDetail.module.less";
import ItemImage from "./layout/ItemImage";
import OptionList from "./layout/OptionList";
export default function ItemDetail() {
const [selectedPatnrId, setSelectedPatnrId] = useState(1);
const [selectedPardtId, setSelectedPardtId] = useState("A523924");
const [productData, setProductData] = useState();
const data = useSelector((state) => state.main.productData);
const dispatch = useDispatch();
useEffect(() => {
dispatch(
getMainCategoryDetail({
patnrId: selectedPatnrId,
prdtId: selectedPardtId,
})
);
}, [dispatch]);
useEffect(() => {
setProductData(data);
// console.log("#groupInfo", productData);
}, [data, productData]);
return (
<TPanel isTabActivated={false}>
<THeader
className={css.header}
title={productData?.prdtNm}
onBackButton
/>
<TBody className={css.container} scrollable={false}>
<ItemImage productData={productData} />
<OptionList />
</TBody>
</TPanel>
);
}

View File

@@ -0,0 +1,24 @@
@import '../../style/CommonStyle.module.less';
@import "../../style/utils.module.less";
.header {
> div {
.font(@fontFamily: @baseFontBold, @fontSize: 30px) !important;
}
display: flex;
width: 100%;
height: 90px;
background-color: #f2f2f2;
align-items: center;
color: #333333;
padding: 0 0 0 60px;
}
.container {
display: flex;
justify-content: space-between;
}

View File

@@ -0,0 +1,72 @@
import React, { useEffect, useState } from "react";
import Image from "@enact/sandstone/Image";
import Spottable from "@enact/spotlight/Spottable";
import css from "./Indicator.module.less";
const SpottableComponent = Spottable("button");
export default function Indicator({ images }) {
const [selectedImage, setSelectedImage] = useState(null);
const [selectedIndex, setSelectedIndex] = useState(0);
useEffect(() => {
if (images && images.length > 0) {
setSelectedImage(images[0]);
}
}, [images]);
const handlePrevClick = () => {
setSelectedImage((prevSelectedImage) => {
const currentIndex = images.indexOf(prevSelectedImage);
return currentIndex === 0
? images[images.length - 1]
: images[currentIndex - 1];
});
};
const handleNextClick = () => {
setSelectedImage((prevSelectedImage) => {
const currentIndex = images.indexOf(prevSelectedImage);
return currentIndex === images.length - 1
? images[0]
: images[currentIndex + 1];
});
};
const handleItemClick = (image) => {
setSelectedImage(image);
};
return (
<div className={css.Wrap}>
<div>
{images && <img src={selectedImage} alt="" className={css.thumbnail} />}
</div>
<div>
<SpottableComponent
onClick={handlePrevClick}
className={css.upButton}
/>
{images &&
images
.slice(0, 3)
.map((image, index) => (
<Image
key={index}
src={image}
alt={""}
onClick={() => handleItemClick(image)}
selected={selectedIndex === index}
className={css.image}
/>
))}
<SpottableComponent
className={css.downButton}
onClick={handleNextClick}
/>
</div>
</div>
);
}

View File

@@ -0,0 +1,46 @@
@import '../../../style/CommonStyle.module.less';
@import "../../../style/utils.module.less";
.Wrap {
margin: 30px 0 0 10px;
height: 560px;
display: flex;
.thumbnail {
.size(@w: 560px, @h: 560px);
border: solid 1px #dadada;
// background-color: #fff;
//margin: 30px 10px 10px 0 ;
background-color: red;
}
.upButton {
.size(@w: 144px , @h: 48px);
border: none;
margin-bottom: 4px;
background-position: center;
background-size: cover;
background-image: url('../../../../assets/button/etc/btn-bk-up-nor.svg');
}
.downButton {
.size(@w: 144px , @h: 48px);
border: none;
margin-top: 10px;
background-position: center;
background-size: cover;
background-image: url('../../../../assets/button/etc/btn-bk-down-nor.svg');
}
.image {
margin: 0;
// border: solid 1px #dadada;
.size(@w: 140px , @h: 140px);
}
}

View File

@@ -0,0 +1,32 @@
import React, { useEffect, useState } from "react";
import Spottable from "@enact/spotlight/Spottable";
import { $L } from "../../../utils/helperMethods";
import Indicator from "../components/Indicator";
import css from "../layout/ItemImage.module.less";
const SpottableComponent = Spottable("div");
export default function ItemImage({ productData }) {
const [images, setImages] = useState();
useEffect(() => {
if (productData) {
setImages(productData?.imgUrls);
}
}, [productData, images]);
return (
<SpottableComponent className={css.Wrap}>
<Indicator images={images} />
<div className={css.order}>
<div>{$L("Call to Order")}</div>
<div className={css.icon}>
<span />
<div>{productData?.orderPhnNo}</div>
</div>
</div>
</SpottableComponent>
);
}

View File

@@ -0,0 +1,43 @@
@import '../../../style/CommonStyle.module.less';
@import "../../../style/utils.module.less";
.Wrap {
padding-left: 120px;
.size(@w: 894px, @h: 930px);
.thumbnail {
.size(@w: 560px, @h: 560px);
border: solid 1px #dadada;
background-color: #fff;
margin: 30px 10px 10px 0 ;
}
.order {
.font(@fontFamily: @baseFontBold, @fontSize: 30px);
.size(@w: 560px, @h: 68px);
display: flex;
justify-content: space-between;
line-height: 68px;
background-color: #f2f2f2;
> div {
padding: 0 33px 0 30px;
}
}
.icon {
display: flex;
> span {
.size(@w: 42px, @h: 42px);
background-image: url("../../../../assets/icon/ic-gr-call.svg");
background-size: cover;
background-position: center;
margin: 13px 0 0 4px ;
}
}
.indicator{
// display: flex;
}
}

View File

@@ -0,0 +1,10 @@
import React from "react";
import Spottable from "@enact/spotlight/Spottable";
import css from "./OptionList.module.less";
const SpottableComponent = Spottable("div");
export default function OptionList() {
return <SpottableComponent className={css.Wrap}></SpottableComponent>;
}

View File

@@ -0,0 +1,7 @@
@import '../../../style/CommonStyle.module.less';
@import '../../../style/utils.module.less';
.Wrap {
.size(@w: 1026px, @h: 930px);
background-color: #f8f8f8;
}