diff --git a/com.twin.app.shoptime/assets/images/partners/ic-tab-partners-focus@3x.png b/com.twin.app.shoptime/assets/images/partners/ic-tab-partners-focus@3x.png
new file mode 100644
index 00000000..5243a60f
Binary files /dev/null and b/com.twin.app.shoptime/assets/images/partners/ic-tab-partners-focus@3x.png differ
diff --git a/com.twin.app.shoptime/assets/images/partners/ic-tab-partners-selected@3x.png b/com.twin.app.shoptime/assets/images/partners/ic-tab-partners-selected@3x.png
new file mode 100644
index 00000000..c8495310
Binary files /dev/null and b/com.twin.app.shoptime/assets/images/partners/ic-tab-partners-selected@3x.png differ
diff --git a/com.twin.app.shoptime/src/App/App.module.less b/com.twin.app.shoptime/src/App/App.module.less
index f9db12c8..5c06eac1 100644
--- a/com.twin.app.shoptime/src/App/App.module.less
+++ b/com.twin.app.shoptime/src/App/App.module.less
@@ -1,3 +1,71 @@
-.app {
- /* styles can be put here */
+/* Reset */
+* {
+ box-sizing: border-box;
+ color: #1a1a1a;
+}
+body,
+div,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+table,
+th,
+td,
+ul,
+ol,
+li,
+dl,
+dt,
+dd,
+p,
+form,
+input,
+fieldset {
+ padding: 0;
+ margin: 0;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 100%;
+ font-weight: normal;
+}
+ol,
+ul,
+li {
+ list-style: none;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section,
+header {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ display: block;
+}
+
+img {
+ vertical-align: top;
+}
+
+button {
+ background: none;
+ border: 0;
+ padding: 0;
}
diff --git a/com.twin.app.shoptime/src/api/apiConfig.js b/com.twin.app.shoptime/src/api/apiConfig.js
index e335edf2..ffdcdc6d 100644
--- a/com.twin.app.shoptime/src/api/apiConfig.js
+++ b/com.twin.app.shoptime/src/api/apiConfig.js
@@ -12,7 +12,12 @@ export const URLS = {
//home controller
GET_HOME_TERMS: "/lgsp/v1/home/terms.lge",
- GET_HOME_MENU: "/lgsp/v1/home/menu.lge",
+
+ //brand-controller
+ GET_BRAND_LIST: "/lgsp/v1/brand/info.lge",
+ GET_BRAND_LAYOUT_INFO: "/lgsp/v1/brand/shelf.lge",
+ GET_BRAND_LIVE_CHANNEL_INFO: "/lgsp/v1/brand/live.lge",
+ GET_BRAND_TODAYS_DEALS: "/lgsp/v1/brand/tsv.lge",
//on-sale controller
GET_ON_SALE_INFO: "/lgsp/v1/onsale/onsale.lge",
diff --git a/com.twin.app.shoptime/src/api/deviceApi.js b/com.twin.app.shoptime/src/api/deviceApi.js
index 9aab3046..64aa4016 100644
--- a/com.twin.app.shoptime/src/api/deviceApi.js
+++ b/com.twin.app.shoptime/src/api/deviceApi.js
@@ -8,8 +8,16 @@ export async function getAuthenticationCode() {
return response.data;
} catch (error) {
- console.error("Error : ", error);
- throw error;
+ const { response } = error;
+
+ if (response) {
+ const statusCode = response.status;
+ const statusText = response.statusText;
+
+ console.error(`${statusCode} Error, ${statusText}`);
+ }
+
+ throw new Error(error);
}
}
diff --git a/com.twin.app.shoptime/src/api/featuredBrands.js b/com.twin.app.shoptime/src/api/featuredBrands.js
new file mode 100644
index 00000000..dfe8ccf7
--- /dev/null
+++ b/com.twin.app.shoptime/src/api/featuredBrands.js
@@ -0,0 +1,96 @@
+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);
+ }
+}
diff --git a/com.twin.app.shoptime/src/components/TabLayout/TabLayout.jsx b/com.twin.app.shoptime/src/components/TabLayout/TabLayout.jsx
index 14c428e4..78dc203f 100644
--- a/com.twin.app.shoptime/src/components/TabLayout/TabLayout.jsx
+++ b/com.twin.app.shoptime/src/components/TabLayout/TabLayout.jsx
@@ -1,33 +1,41 @@
-import Spotlight from "@enact/spotlight";
-import classNames from "classnames";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
-} from "react";
-import { useDispatch, useSelector } from "react-redux";
-import TabItem from "./TabItem";
-import css from "./TabLayout.module.less";
-//enact
-import Skinnable from "@enact/sandstone/Skinnable";
-import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
-import { Cancelable } from "@enact/ui/Cancelable";
+} from 'react';
+
+import classNames from 'classnames';
+import {
+ useDispatch,
+ useSelector,
+} from 'react-redux';
+
//아이콘
-import { Job } from "@enact/core/util";
-import CartIcon from "./iconComponents/CartIcon";
-import CategoryIcon from "./iconComponents/CategoryIcon";
-import FeaturedBrandIcon from "./iconComponents/FeaturedBrandIcon";
-import HomeIcon from "./iconComponents/HomeIcon";
-import HotPicksIcon from "./iconComponents/HotPicksIcon";
-import MyPageIcon from "./iconComponents/MyPageIcon";
-import OnSaleIcon from "./iconComponents/OnSaleIcon";
-import SearchIcon from "./iconComponents/SearchIcon";
-import TrendingNowIcon from "./iconComponents/TrendingNowIcon";
+import { Job } from '@enact/core/util';
+//enact
+import Skinnable from '@enact/sandstone/Skinnable';
+import Spotlight from '@enact/spotlight';
+import SpotlightContainerDecorator
+ from '@enact/spotlight/SpotlightContainerDecorator';
+import { Cancelable } from '@enact/ui/Cancelable';
+
//이미지
-import shoptimeFullIcon from "../../../assets/icons/ic-lnb-logo-shoptime@3x.png";
-import shopTimeIcon from "../../../assets/icons/ic-lnb-shoptime-symbol@3x.png";
+import shoptimeFullIcon
+ from '../../../assets/icons/ic-lnb-logo-shoptime@3x.png';
+import shopTimeIcon from '../../../assets/icons/ic-lnb-shoptime-symbol@3x.png';
+import CartIcon from './iconComponents/CartIcon';
+import CategoryIcon from './iconComponents/CategoryIcon';
+import FeaturedBrandIcon from './iconComponents/FeaturedBrandIcon';
+import HomeIcon from './iconComponents/HomeIcon';
+import HotPicksIcon from './iconComponents/HotPicksIcon';
+import MyPageIcon from './iconComponents/MyPageIcon';
+import OnSaleIcon from './iconComponents/OnSaleIcon';
+import SearchIcon from './iconComponents/SearchIcon';
+import TrendingNowIcon from './iconComponents/TrendingNowIcon';
+import TabItem from './TabItem';
+import css from './TabLayout.module.less';
const Container = SpotlightContainerDecorator(
{ enterTo: "default-element" },
@@ -270,6 +278,7 @@ export default function TabLayout({ topPanelName, onTabActivated }) {
const handleNavigation = useCallback(
({ index, target }) => {
+ console.log(index, target);
setMainSelectedIndex(index);
if (target) {
// dispatch(resetPanels(target));
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.jsx
new file mode 100644
index 00000000..fee41aa9
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.jsx
@@ -0,0 +1,18 @@
+import React from "react";
+
+import css from "./Banner.module.less";
+
+export default function Banner({ selectedBrandInfo, topImgInfo }) {
+ const { logoImgAlt, logoImgPath, patncNm } = selectedBrandInfo;
+ const { topImgAlt, topImgNm, topImgPath } = topImgInfo;
+
+ return (
+
+
+

+
{patncNm}
+
+

+
+ );
+}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.module.less
new file mode 100644
index 00000000..154cf0e2
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.module.less
@@ -0,0 +1,33 @@
+@import "../../../style/CommonStyle.module.less";
+@import "../../../style/utils.module.less";
+
+.container {
+ position: relative;
+ .flex(@justifyCenter: flex-start, @alignCenter: flex-end);
+ .size(@w: 100%, @h: 108px);
+
+ div {
+ .flex();
+ gap: 12px;
+
+ // @@ Todo, padding-left 전체적으로 적용유무 수정
+ padding-left: 60px;
+
+ h2 {
+ color: #fff;
+ font-family: @baseFontBold;
+ font-size: 36px;
+ }
+ img {
+ .size(@w: 60px, @h: 60px);
+ }
+ }
+
+ > img {
+ .position(@position: absolute);
+ width: 100%;
+ height: 438px;
+ object-fit: cover;
+ z-index: -1;
+ }
+}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/package.json b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/package.json
new file mode 100644
index 00000000..4780a2cd
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/package.json
@@ -0,0 +1,6 @@
+{
+ "main": "Banner.jsx",
+ "styles": [
+ "Banner.module.less"
+ ]
+}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx
index da72921e..b8d7bb96 100644
--- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx
@@ -1,5 +1,107 @@
+import React, { useEffect, useState } from "react";
+
+import {
+ getBrandLayoutInfo,
+ getBrandList,
+ getBrandLiveChannelInfo,
+} from "../../api/featuredBrands";
import TPanel from "../../components/TPanel/TPanel";
+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
+
+*/
export default function FeaturedBrandsPanel() {
- return Featured Brands;
+ // @@ Todo, provided by GNB as props or global state
+ const [brandList, setBrandList] = useState([]);
+
+ // @@ Todo, provided by GNB as props or global state
+ const [selectedBrandId, setSelectedBrandId] = useState("1");
+
+ const [selectedBrandInfo, setSelectedBrandInfo] = useState({});
+ const [topImgInfo, setTopImgInfo] = useState({});
+
+ 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);
+ }
+ };
+
+ useEffect(() => {
+ // @@ Todo, provided by GNB as props or global state
+ setSelectedBrandId("1");
+ fetchBrandList();
+ }, []);
+
+ useEffect(() => {
+ fetchBrandLayoutInfo(selectedBrandId);
+ fetchBrandLiveChannelInfo(selectedBrandId);
+ }, [selectedBrandId]);
+
+ return (
+ /* scenario page 98 */
+
+ {brandList && brandList.length > 1 && (
+
+ )}
+
+
+
+ );
}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.module.less
index e69de29b..a7b63b33 100644
--- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.module.less
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.module.less
@@ -0,0 +1,3 @@
+.container {
+ margin-left: 120px;
+}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenu.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenu.jsx
new file mode 100644
index 00000000..e7ef4c99
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenu.jsx
@@ -0,0 +1,60 @@
+import React, { useEffect } from 'react';
+
+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';
+
+const Container = SpotlightContainerDecorator(
+ { leaveFor: { right: "" }, enterTo: "last-focused" },
+ "nav"
+);
+
+const SpottableComponent = Spottable("li");
+
+export default function QuickMenu({
+ brandList,
+ selectedBrandId,
+ onQuickMenuClick,
+ ...rest
+}) {
+ const handleClick = (patnrId) => {
+ if (selectedBrandId === patnrId) {
+ return;
+ }
+
+ onQuickMenuClick(patnrId);
+ };
+
+ useEffect(() => {
+ const element = document.getElementById(selectedBrandId);
+ Spotlight.focus(element);
+ }, []);
+
+ return (
+
+
+ {brandList &&
+ brandList.map(({ logoImgAlt, logoImgPath, newFlag, patnrId }) => {
+ return (
+ handleClick(patnrId)}
+ >
+ {newFlag === "Y" && (
+ {$L("NEW")}
+ )}
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenu.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenu.module.less
new file mode 100644
index 00000000..08ddf19e
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenu.module.less
@@ -0,0 +1,67 @@
+@import "../../../style/CommonStyle.module.less";
+@import "../../../style/utils.module.less";
+
+.container {
+ .flex(@justifyCenter:flex start);
+ .size(@w: 100%, @h: 180px);
+ padding: 30px 60px;
+ background-color: #e7e7e7;
+
+ ul {
+ .flex(@justifyCenter: flex-start);
+ gap: 18px;
+
+ /* normal */
+ li {
+ position: relative;
+
+ .newBagde {
+ .position(@position: absolute, @top: 0, @right: 0, @bottom: auto, @left: auto);
+ z-index: 10;
+ .size(@w: 60px, @h: 30px);
+ background-color: #f00;
+ color: #fff;
+ border-radius: 6px;
+ font-family: Arial;
+ font-weight: 600;
+ font-size: 18px;
+ text-align: center;
+ }
+
+ .outline {
+ .position(@position: absolute, @top: 0, @right: auto, @bottom: auto, @left: 0);
+ .size(@w: 138px, @h:138px);
+ background-position: center;
+ background-size: cover;
+ }
+
+ img {
+ .size(@w: 120px, @h: 120px);
+ }
+
+ /* focused */
+ &:focus {
+ border-radius: 60px;
+ .focusDropShadow();
+
+ .outline {
+ background-image: url("../../../../assets/images/partners/ic-tab-partners-focus@3x.png");
+ }
+
+ img {
+ .size(@w: 138px, @h: 138px);
+ }
+ }
+
+ /* selected */
+ &.selected {
+ img {
+ .size(@w: 138px, @h: 138px);
+ }
+ .outline {
+ background-image: url("../../../../assets/images/partners/ic-tab-partners-selected@3x.png");
+ }
+ }
+ }
+ }
+}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/package.json b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/package.json
new file mode 100644
index 00000000..125fb9f7
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/package.json
@@ -0,0 +1,6 @@
+{
+ "main": "QuickMenu.jsx",
+ "styles": [
+ "QuickMenu.module.less"
+ ]
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/CategoryNav.jsx b/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/CategoryNav.jsx
new file mode 100644
index 00000000..84316bdb
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/CategoryNav.jsx
@@ -0,0 +1,55 @@
+import React from "react";
+
+import classNames from "classnames";
+
+import Scroller from "@enact/sandstone/Scroller";
+import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
+import Spottable from "@enact/spotlight/Spottable";
+
+import css from "./CategoryNav.module.less";
+
+const Container = SpotlightContainerDecorator(
+ { leaveFor: { right: "" }, enterTo: "last-focused" },
+ "nav"
+);
+
+const SpottableComponent = Spottable("li");
+
+export default function CategoryNav({
+ categoryInfos,
+ currentLgCatCdIndex,
+ onCategoryNavClick,
+ ...rest
+}) {
+ return (
+
+
+
+ {categoryInfos &&
+ categoryInfos.map(({ lgCatNm, lgCatCd }, index) => {
+ return (
+ onCategoryNavClick(lgCatCd, index)}
+ >
+
+
+
+ {lgCatNm}
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/CategoryNav.module.less b/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/CategoryNav.module.less
new file mode 100644
index 00000000..31b3130b
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/CategoryNav.module.less
@@ -0,0 +1,214 @@
+@import "../../../style/CommonStyle.module.less";
+@import "../../../style/utils.module.less";
+
+.container {
+ position: fixed;
+ top: 0;
+ left: 120px;
+
+ // @@ Todo, z-index 관련 추후 수정 염두
+ z-index: 10;
+ width: calc(100% - 120px);
+ background-color: @BG_COLOR_01;
+ font-family: @baseFontBold;
+ font-size: 28px;
+ // @@ font 관련 업데이트 후 수정
+ // .font(@fontFamily: @baseFontBold, @fontSize: 28px);
+
+ ul {
+ display: flex;
+ gap: 30px;
+ margin-top: 50px;
+ }
+
+ .category {
+ /* normal */
+ position: relative;
+ .flex(@direction: column);
+ min-width: 210px;
+ color: #1a1a1a;
+ text-align: center;
+
+ div {
+ .flex();
+ .size(@w: 94px, @h: 94px);
+ margin-bottom: 18px;
+ border-radius: 48px;
+ background-color: #1a1a1a;
+ border: 2px solid #1a1a1a;
+
+ span {
+ .size(@w: 80px, @h:80px);
+ background-position: center;
+ background-size: cover;
+
+ &.category-icon-1017 {
+ // LG Electronics
+ background-image: url("../../../../assets/category/ic-category-lgelectronics-nor@3x.png");
+ }
+
+ // Garden and Outdoors
+ &.category-icon-1008 {
+ background-image: url("../../../../assets/category/ic-category-garden-nor@3x.png");
+ }
+
+ // Fashion
+ &.category-icon-1000 {
+ background-image: url("../../../../assets/category/ic-category-fashion-nor@3x.png");
+ }
+
+ // Beauty
+ &.category-icon-1003 {
+ background-image: url("../../../../assets/category/ic-category-beauty-nor@3x.png");
+ }
+
+ // Jewelry
+ &.category-icon-1004 {
+ background-image: url("../../../../assets/category/ic-category-jewelry-nor@3x.png");
+ }
+
+ // Home
+ &.category-icon-1006 {
+ background-image: url("../../../../assets/category/ic-category-home-nor@3x.png");
+ }
+
+ // Kitchen & Food
+ &.category-icon-1007 {
+ background-image: url("../../../../assets/category/ic-category-kitchen-nor@3x.png");
+ }
+
+ // Accessories
+ &.category-icon-1014 {
+ background-image: url("../../../../assets/category/ic-category-accessories-nor@3x.png");
+ }
+
+ // Heaclth & Fitness
+ &.category-icon-1009 {
+ background-image: url("../../../../assets/category/ic-category-health-nor@3x.png");
+ }
+
+ // Crafts & Sewing
+ &.category-icon-1011 {
+ background-image: url("../../../../assets/category/ic-category-cw-nor@3x.png");
+ }
+
+ // Electronics
+ &.category-icon-1010 {
+ background-image: url("../../../../assets/category/ic-category-electronics-nor@3x.png");
+ }
+
+ // Clearance
+ &.category-icon-1013 {
+ background-image: url("../../../../assets/category/ic-category-clearance-nor@3x.png");
+ }
+ }
+ }
+
+ strong {
+ height: 60px;
+ margin-bottom: 12px;
+ }
+
+ &::after {
+ content: "";
+ position: absolute;
+ bottom: 0;
+ display: block;
+ .size(@w: 100%, @h:6px);
+ background-color: transparent;
+ }
+
+ /* focused */
+ &:focus {
+ // color: #c70850;
+
+ div {
+ background-color: #fff;
+ border: solid 2px #c70850;
+ box-shadow: 0 0 25px 0 rgba(2, 3, 3, 0.8);
+
+ span {
+ // LG Electronics
+ &.category-icon-1017 {
+ background-image: url("../../../../assets/category/ic-category-lgelectronics-foc@3x.png");
+ }
+
+ // Garden and Outdoors
+ &.category-icon-1008 {
+ background-image: url("../../../../assets/category/ic-category-garden-foc@3x.png");
+ }
+
+ // Fashion
+ &.category-icon-1000 {
+ background-image: url("../../../../assets/category/ic-category-fashion-foc@3x.png");
+ }
+
+ // Beauty
+ &.category-icon-1003 {
+ background-image: url("../../../../assets/category/ic-category-beauty-foc@3x.png");
+ }
+
+ // Jewelry
+ &.category-icon-1004 {
+ background-image: url("../../../../assets/category/ic-category-jewelry-foc@3x.png");
+ }
+
+ // Home
+ &.category-icon-1006 {
+ background-image: url("../../../../assets/category/ic-category-home-foc@3x.png");
+ }
+
+ // Kitchen & Food
+ &.category-icon-1007 {
+ background-image: url("../../../../assets/category/ic-category-kitchen-foc@3x.png");
+ }
+
+ // Accessories
+ &.category-icon-1014 {
+ background-image: url("../../../../assets/category/ic-category-accessories-foc@3x.png");
+ }
+
+ // Heaclth & Fitness
+ &.category-icon-1009 {
+ background-image: url("../../../../assets/category/ic-category-health-foc@3x.png");
+ }
+
+ // Crafts & Sewing
+ &.category-icon-1011 {
+ background-image: url("../../../../assets/category/ic-category-cw-foc@3x.png");
+ }
+
+ // Electronics
+ &.category-icon-1010 {
+ background-image: url("../../../../assets/category/ic-category-electronics-foc@3x.png");
+ }
+
+ // Clearance
+ &.category-icon-1013 {
+ background-image: url("../../../../assets/category/ic-category-clearance-foc@3x.png");
+ }
+ }
+ }
+
+ strong {
+ color: #c70850;
+ }
+
+ &::after {
+ background-color: #c70850;
+ }
+ }
+ }
+
+ /* selected */
+ .selected {
+ div {
+ background-color: #c70850;
+ border: solid 2px #c70850;
+ }
+
+ strong {
+ color: #c70850;
+ }
+ }
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/package.json b/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/package.json
new file mode 100644
index 00000000..cc1575e5
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/CategoryNav/package.json
@@ -0,0 +1,6 @@
+{
+ "main": "CategoryNav.jsx",
+ "styles": [
+ "CategoryNav.module.less"
+ ]
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx b/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx
index 0bf9ca3f..702e7a51 100644
--- a/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx
@@ -1,5 +1,87 @@
+import React, { useEffect, useState } from "react";
+
+import { useDispatch } from "react-redux";
+
+import { getOnSaleInfo } from "../../api/onSaleApi";
import TPanel from "../../components/TPanel/TPanel";
+import CategoryNav from "./CategoryNav/CategoryNav";
+import css from "./OnSalePanel.module.less";
+import OnSaleProductCard from "./OnSaleProductCard/OnSaleProductCard";
+import OnSaleProductsGrid from "./OnSaleProductsGrid/OnSaleProductsGrid";
export default function OnSalePanel() {
- return OnSale;
+ const dispatch = useDispatch();
+
+ const [currentLgCatCdIndex, setCurrentLgCatCdIndex] = useState(0);
+ const [lgCatCd, setLgCatCd] = useState("");
+ const [categoryInfos, setCategoryInfos] = useState([]);
+ const [saleInfos, setSaleInfos] = useState([]);
+ const [saleProductInfos, setSaleProductInfos] = useState([]);
+
+ const handleCategoryNav = (lgCatCd, index) => {
+ if (currentLgCatCdIndex === index) {
+ return;
+ }
+ setCurrentLgCatCdIndex(index);
+ setLgCatCd(lgCatCd);
+ };
+
+ const getOnSaleInfoData = async (lgCatCd) => {
+ try {
+ const onSaleInfoData = await getOnSaleInfo({
+ lgCatCd,
+ categoryIncFlag: "Y",
+ });
+ console.log("On Sale Data:", onSaleInfoData);
+
+ if (onSaleInfoData) {
+ if (lgCatCd === "") {
+ setCategoryInfos(onSaleInfoData.categoryInfos);
+ }
+
+ setSaleInfos(onSaleInfoData.saleInfos);
+
+ let saleProducts = [];
+
+ for (let index = 0; index < onSaleInfoData.saleInfos.length; index++) {
+ saleProducts.push(onSaleInfoData.saleInfos[index].saleProductInfos);
+ }
+
+ setSaleProductInfos(saleProducts);
+ }
+ } catch (error) {
+ console.error("Error fetching on sale:", error);
+ }
+ };
+
+ useEffect(() => {
+ getOnSaleInfoData(lgCatCd);
+ }, [lgCatCd, currentLgCatCdIndex]);
+
+ return (
+
+
+
+
+ {saleInfos.map(({ saleNm }, index) => {
+ return (
+
+ {saleProductInfos[index].map((saleProduct) => {
+ return (
+
+ );
+ })}
+
+ );
+ })}
+
+
+ );
}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.module.less b/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.module.less
index e69de29b..2f20d531 100644
--- a/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.module.less
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.module.less
@@ -0,0 +1,10 @@
+@import "../../style/CommonStyle.module.less";
+@import "../../style/utils.module.less";
+
+.container {
+ font-family: @baseFont;
+
+ .onSaleProducts {
+ margin-top: 236px;
+ }
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/OnSaleProductCard.jsx b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/OnSaleProductCard.jsx
new file mode 100644
index 00000000..80700f57
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/OnSaleProductCard.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+
+import Spottable from "@enact/spotlight/Spottable";
+
+import css from "./OnSaleProductCard.module.less";
+
+const SpottableComponent = Spottable("li");
+
+export default function OnSaleProductCard({ saleProduct, ...rest }) {
+ const { imgUrl, prdtId, prdtNm, priceInfo } = saleProduct;
+
+ const originalPrice = priceInfo.split("|")[0];
+ const salePrice = priceInfo.split("|")[1];
+ const salePercentage = priceInfo.split("|").reverse()[0];
+
+ return (
+
+
+

+ {salePercentage &&
{salePercentage}}
+
+
+
{prdtNm}
+
+ {salePrice}
+ {originalPrice}
+
+
+
+ );
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/OnSaleProductCard.module.less b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/OnSaleProductCard.module.less
new file mode 100644
index 00000000..b3df69ae
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/OnSaleProductCard.module.less
@@ -0,0 +1,84 @@
+@import "../../../style/CommonStyle.module.less";
+@import "../../../style/utils.module.less";
+
+.container {
+ position: relative;
+ .flex(@direction: column);
+ .size(@w: 260px, @h: 375px);
+ margin: 0 10px;
+ border-radius: 10px;
+ background-color: #e3e7ee;
+
+ div:nth-child(1) {
+ position: relative;
+ .size(@w: 260px, @h: 260px);
+
+ img {
+ .size(@w: 260px, @h: 260px);
+ object-fit: cover;
+ border-top-right-radius: 10px;
+ border-top-left-radius: 10px;
+ }
+
+ > span {
+ .position(@position: absolute, @top: auto, @right: auto, @bottom: 12px, @left: 12px);
+ .size(@w: 44px, @h: 44px);
+ border-radius: 44px;
+ background-color: #c70850;
+ color: #fff;
+ font-family: @baseFontBold;
+ font-size: 20px;
+ line-height: 44px;
+ text-align: center;
+ }
+ }
+
+ div:nth-child(2) {
+ .flex(@direction: column, @alignCenter: flex-start);
+ gap: 10px;
+ padding: 10px 15px;
+
+ h3 {
+ display: -webkit-box;
+ font-family: @baseFontBold;
+ font-size: 24px;
+ line-height: 26px;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ }
+ p {
+ color: #c70850;
+ font-family: @baseFontBold;
+ font-size: 24px;
+
+ span {
+ margin-left: 5px;
+ color: #767676;
+ font-size: 18px;
+ text-decoration: line-through;
+ }
+ }
+ }
+
+ &::after {
+ content: "";
+ .position(@position: absolute, @top: -12px, @right: auto, @bottom: auto, @left: -12px);
+ display: block;
+ .size(@w: 276px, @h: 390px);
+ border: 4px solid transparent;
+ border-radius: 10px;
+ }
+
+ &:focus {
+ background-color: #fcfcfc;
+
+ &::after {
+ border: 4px solid #c70850;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0),
+ 0px 9px 15px 0 rgba(0, 0, 0, 0.3);
+ }
+ }
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/package.json b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/package.json
new file mode 100644
index 00000000..a8606fc6
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductCard/package.json
@@ -0,0 +1,6 @@
+{
+ "main": "OnSaleProductCard.jsx",
+ "styles": [
+ "OnSaleProductCard.module.less"
+ ]
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/OnSaleProductsGrid.jsx b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/OnSaleProductsGrid.jsx
new file mode 100644
index 00000000..a649bf6e
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/OnSaleProductsGrid.jsx
@@ -0,0 +1,19 @@
+import React from "react";
+
+import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
+
+import css from "./OnSaleProductsGrid.module.less";
+
+const Container = SpotlightContainerDecorator(
+ { leaveFor: { left: "", right: "" }, enterTo: "last-focused" },
+ "li"
+);
+
+export default function OnSaleProductsGrid({ saleNm, children }) {
+ return (
+
+ {saleNm}
+
+
+ );
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/OnSaleProductsGrid.module.less b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/OnSaleProductsGrid.module.less
new file mode 100644
index 00000000..90282200
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/OnSaleProductsGrid.module.less
@@ -0,0 +1,20 @@
+@import "../../../style/CommonStyle.module.less";
+@import "../../../style/utils.module.less";
+
+.container {
+ .flex(@direction:column, @alignCenter:flex-start);
+ gap: 40px;
+ width: 100%;
+ padding: 40px 0 20px 0;
+
+ h2 {
+ margin-left: 60px;
+ font-family: @baseFontBold;
+ font-size: 38px;
+ }
+
+ ul {
+ .flex();
+ margin-left: 50px;
+ }
+}
diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/package.json b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/package.json
new file mode 100644
index 00000000..1aba1243
--- /dev/null
+++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleProductsGrid/package.json
@@ -0,0 +1,6 @@
+{
+ "main": "OnSaleProductsGrid.jsx",
+ "styles": [
+ "OnSaleProductsGrid.module.less"
+ ]
+}