TAxios에 Device Header 임시값 추가, TInput, SearchPanel 작업 중
This commit is contained in:
@@ -99,6 +99,23 @@ export const TAxios = async (
|
|||||||
AUTHORIZATION.headers["sdk_ver"] = "1.0.0";
|
AUTHORIZATION.headers["sdk_ver"] = "1.0.0";
|
||||||
AUTHORIZATION.headers["publish_flag"] = "Y";
|
AUTHORIZATION.headers["publish_flag"] = "Y";
|
||||||
|
|
||||||
|
// Device Header 임시
|
||||||
|
AUTHORIZATION.headers["X-Authentication"] = "MkOLvUocrJ69RH/iV1ZABJhjR2g=";
|
||||||
|
AUTHORIZATION.headers["X-Device-ID"] =
|
||||||
|
"OemUY5qbPITZv96QKlxrtcqT6ypeX6us2qANLng3/0QCUhv2mecK1UDTMYb/hjpjey9dC/kFycc/5R8u+oK56JIWyYC4V278z64YDPKbDXIsd+eECvyf+Rdm8BneIUPM";
|
||||||
|
AUTHORIZATION.headers["X-Device-Product"] = "webOSTV 5.0";
|
||||||
|
AUTHORIZATION.headers["X-Device-Platform"] = "W20P";
|
||||||
|
AUTHORIZATION.headers["X-Device-Model"] = "HE_DTV_W20P_AFADATAA";
|
||||||
|
AUTHORIZATION.headers["X-Device-Eco-Info"] = "1";
|
||||||
|
AUTHORIZATION.headers["X-Device-Country"] = "US";
|
||||||
|
AUTHORIZATION.headers["X-Device-Language"] = "en-US";
|
||||||
|
AUTHORIZATION.headers["X-Device-Netcast-Platform-Version"] = "5.0.0";
|
||||||
|
AUTHORIZATION.headers["X-Device-Publish-Flag"] = "N";
|
||||||
|
AUTHORIZATION.headers["X-Device-Fck"] = "253";
|
||||||
|
AUTHORIZATION.headers["X-Device-Eula"] =
|
||||||
|
"additionalDataAllowed,takeOnAllowed,networkAllowed,generalTermsAllowed,chpAllowed,customAdAllowed,acrOnAllowed,voice2Allowed,voiceAllowed,acrAdAllowed";
|
||||||
|
AUTHORIZATION.headers["X-Device-Personalization"] = "Y";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
baseUrl !== URLS.GET_AUTHENTICATION_CODE &&
|
baseUrl !== URLS.GET_AUTHENTICATION_CODE &&
|
||||||
!accessToken &&
|
!accessToken &&
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ export const URLS = {
|
|||||||
//my-page controller
|
//my-page controller
|
||||||
GET_MY_RECOMMANDED_KEYWORD: "/lgsp/v1/mypage/reckeyword.lge",
|
GET_MY_RECOMMANDED_KEYWORD: "/lgsp/v1/mypage/reckeyword.lge",
|
||||||
|
|
||||||
|
//search controller
|
||||||
|
GET_SEARCH: "/lgsp/v1/search/list.lge",
|
||||||
|
|
||||||
//main controller
|
//main controller
|
||||||
GET_SUB_CATEGORY: "/lgsp/v1/main/subcategory.lge",
|
GET_SUB_CATEGORY: "/lgsp/v1/main/subcategory.lge",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import { InputField } from "@enact/sandstone/Input";
|
|||||||
import Spottable from "@enact/spotlight/Spottable";
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
|
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
import TIconButton from "../TIconButton/TIconButton";
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator("div");
|
const Container = SpotlightContainerDecorator("div");
|
||||||
|
|
||||||
const SpottableInputIcon = Spottable("div");
|
|
||||||
|
|
||||||
const KINDS = { withIcon: "withIcon" };
|
const KINDS = { withIcon: "withIcon" };
|
||||||
const ICONS = { search: "search" };
|
const ICONS = { search: "search" };
|
||||||
const BORDER = { none: "none" };
|
const BORDER = { none: "none" };
|
||||||
@@ -21,10 +20,10 @@ export default function TInput({
|
|||||||
color,
|
color,
|
||||||
className,
|
className,
|
||||||
spotlightDisabled,
|
spotlightDisabled,
|
||||||
|
disabled,
|
||||||
|
onIconClick,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const handleIconClick = useCallback(() => {}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
className={classNames(css.container, className ? className : null)}
|
className={classNames(css.container, className ? className : null)}
|
||||||
@@ -33,12 +32,11 @@ export default function TInput({
|
|||||||
{...rest}
|
{...rest}
|
||||||
spotlightDisabled={spotlightDisabled}
|
spotlightDisabled={spotlightDisabled}
|
||||||
className={classNames(css.input)}
|
className={classNames(css.input)}
|
||||||
spotlightId="input"
|
|
||||||
/>
|
/>
|
||||||
{kind === "withIcon" && (
|
{kind === "withIcon" && (
|
||||||
<SpottableInputIcon
|
<TIconButton
|
||||||
className={classNames(icon && css[icon])}
|
className={classNames(icon && css[icon])}
|
||||||
onClick={handleIconClick}
|
onClick={onIconClick}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
51
com.twin.app.shoptime/src/features/search/searchSlice.js
Normal file
51
com.twin.app.shoptime/src/features/search/searchSlice.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
||||||
|
import { URLS } from "../../api/apiConfig";
|
||||||
|
import { TAxios } from "../../api/TAxios";
|
||||||
|
|
||||||
|
// Search 통합검색 (IBS) 데이터 조회 IF-LGSP-090
|
||||||
|
export const getSearch = createAsyncThunk(
|
||||||
|
"search/getSearch",
|
||||||
|
|
||||||
|
async (params, thunkAPI) => {
|
||||||
|
const { service, query, startIndex, maxResults, domain } = params;
|
||||||
|
|
||||||
|
const onSuccess = (response) => {
|
||||||
|
console.log("getSearch onSuccess: ", response.data);
|
||||||
|
|
||||||
|
thunkAPI.dispatch(updateSearchData(response.data));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFail = (error) => {
|
||||||
|
console.error("getSearch onFail: ", error);
|
||||||
|
};
|
||||||
|
|
||||||
|
TAxios(
|
||||||
|
thunkAPI.dispatch,
|
||||||
|
thunkAPI.getState,
|
||||||
|
"post",
|
||||||
|
URLS.GET_SEARCH,
|
||||||
|
{},
|
||||||
|
{ service, query, startIndex, maxResults, domain },
|
||||||
|
onSuccess,
|
||||||
|
onFail
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
searchDatas: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const searchSlice = createSlice({
|
||||||
|
name: "search",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
updateSearchData: (state, action) => {
|
||||||
|
state.searchDatas = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { updateSearchData } = searchSlice.actions;
|
||||||
|
|
||||||
|
export default searchSlice.reducer;
|
||||||
@@ -7,8 +7,9 @@ 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 myPageReducer from "../features/mypage/myPageSlice";
|
||||||
import onSaleReducer from "../features/onSale/onSaleSlice";
|
import onSaleReducer from "../features/onSale/onSaleSlice";
|
||||||
import productReducer from "../features/product/productSlice";
|
|
||||||
import panelsReducer from "../features/panels/panelsSlice";
|
import panelsReducer from "../features/panels/panelsSlice";
|
||||||
|
import searchReducer from "../features/search/searchSlice";
|
||||||
|
import productReducer from "../features/product/productSlice";
|
||||||
import mainReducer from "../features/main/mainSlice";
|
import mainReducer from "../features/main/mainSlice";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
@@ -21,6 +22,7 @@ export const store = configureStore({
|
|||||||
onSale: onSaleReducer,
|
onSale: onSaleReducer,
|
||||||
brand: brandReducer,
|
brand: brandReducer,
|
||||||
myPage: myPageReducer,
|
myPage: myPageReducer,
|
||||||
|
search: searchReducer,
|
||||||
product: productReducer,
|
product: productReducer,
|
||||||
main: mainReducer,
|
main: mainReducer,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -149,6 +149,14 @@ export default function LoadingPanel({ showLoadingPanel, ...rest }) {
|
|||||||
src={loadingImage}
|
src={loadingImage}
|
||||||
></CustomImage>
|
></CustomImage>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case "wait":
|
||||||
|
return (
|
||||||
|
<section className={css.wait}>
|
||||||
|
<p>LOADING...</p>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,3 +27,14 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wait {
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.flex();
|
||||||
|
|
||||||
|
> p {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import TIconButton, {
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
|
||||||
|
import { changeAppStatus } from "../../features/common/commonSlice";
|
||||||
|
import { getSearch } from "../../features/search/searchSlice";
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
{ enterTo: "last-focused" },
|
{ enterTo: "last-focused" },
|
||||||
@@ -21,12 +25,22 @@ const Container = SpotlightContainerDecorator(
|
|||||||
const ITEMS_PER_PAGE = 9;
|
const ITEMS_PER_PAGE = 9;
|
||||||
|
|
||||||
export default function SearchPanel() {
|
export default function SearchPanel() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const { showLoadingPanel } = useSelector((state) => state.common.appStatus);
|
||||||
const recommandedKeywords = useSelector(
|
const recommandedKeywords = useSelector(
|
||||||
(state) => state.myPage.recommandedKeywordData.data?.keywords
|
(state) => state.myPage.recommandedKeywordData.data?.keywords
|
||||||
);
|
);
|
||||||
|
const searchDatas = useSelector(
|
||||||
|
(state) => state.search.searchDatas.data?.result.results
|
||||||
|
);
|
||||||
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [paginatedKeywords, setPaginatedKeywords] = useState([]);
|
const [paginatedKeywords, setPaginatedKeywords] = useState([]);
|
||||||
|
const [pageChanged, setPageChanged] = useState(false);
|
||||||
|
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [searchPerformed, setSearchPerformed] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (recommandedKeywords) {
|
if (recommandedKeywords) {
|
||||||
@@ -37,26 +51,71 @@ export default function SearchPanel() {
|
|||||||
}
|
}
|
||||||
}, [recommandedKeywords, currentPage]);
|
}, [recommandedKeywords, currentPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pageChanged && paginatedKeywords.length > 0) {
|
||||||
|
const firstButtonSpotlightId = "first-keyword-button";
|
||||||
|
|
||||||
|
Spotlight.focus(firstButtonSpotlightId);
|
||||||
|
|
||||||
|
setPageChanged(false);
|
||||||
|
}
|
||||||
|
}, [pageChanged, paginatedKeywords]);
|
||||||
|
|
||||||
|
const handleSearchChange = useCallback((e) => {
|
||||||
|
const query = e.value;
|
||||||
|
|
||||||
|
if (query.length <= 255) {
|
||||||
|
setSearchQuery(query);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearchSubmit = useCallback(async () => {
|
||||||
|
setSearchPerformed(true);
|
||||||
|
|
||||||
|
if (searchQuery.trim() === "") {
|
||||||
|
setSearchPerformed(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dispatch(
|
||||||
|
getSearch({
|
||||||
|
service: "com.lgshop.app",
|
||||||
|
query: searchQuery,
|
||||||
|
startIndex: 1,
|
||||||
|
maxResults: 10,
|
||||||
|
domain: "theme,show,item",
|
||||||
|
})
|
||||||
|
).unwrap();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Search request failed: ", error);
|
||||||
|
}
|
||||||
|
}, [searchQuery, dispatch]);
|
||||||
|
|
||||||
const handleNext = useCallback(() => {
|
const handleNext = useCallback(() => {
|
||||||
setCurrentPage((prev) => prev + 1);
|
setCurrentPage((prev) => prev + 1);
|
||||||
|
setPageChanged(true);
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
const handlePrev = useCallback(() => {
|
const handlePrev = useCallback(() => {
|
||||||
setCurrentPage((prev) => (prev > 1 ? prev - 1 : prev));
|
setCurrentPage((prev) => (prev > 1 ? prev - 1 : prev));
|
||||||
|
setPageChanged(true);
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
const hasPrevPage = currentPage > 1;
|
const hasPrevPage = currentPage > 1;
|
||||||
const hasNextPage =
|
const hasNextPage =
|
||||||
currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length;
|
currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length;
|
||||||
|
|
||||||
|
const renderContents = () => {
|
||||||
|
if (searchPerformed) {
|
||||||
|
if (searchDatas && searchDatas.length > 0) {
|
||||||
|
return <div>data!!</div>;
|
||||||
|
} else {
|
||||||
|
return <div>SORRY</div>;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return (
|
return (
|
||||||
<TPanel className={css.panel}>
|
<>
|
||||||
<TInput
|
|
||||||
className={css.input}
|
|
||||||
autoFocus
|
|
||||||
kind={KINDS.withIcon}
|
|
||||||
icon={ICONS.search}
|
|
||||||
/>
|
|
||||||
{hasPrevPage && (
|
{hasPrevPage && (
|
||||||
<TIconButton
|
<TIconButton
|
||||||
iconType={ICON_TYPES.leftArrow}
|
iconType={ICON_TYPES.leftArrow}
|
||||||
@@ -66,7 +125,11 @@ export default function SearchPanel() {
|
|||||||
)}
|
)}
|
||||||
<Container className={css.keywordsGrid}>
|
<Container className={css.keywordsGrid}>
|
||||||
{paginatedKeywords.map((keyword, index) => (
|
{paginatedKeywords.map((keyword, index) => (
|
||||||
<TButton key={index} className={css.keywordBox}>
|
<TButton
|
||||||
|
key={index}
|
||||||
|
spotlightId={index === 0 ? "first-keyword-button" : undefined}
|
||||||
|
className={classNames(css.keywordBox)}
|
||||||
|
>
|
||||||
{keyword.keywd}
|
{keyword.keywd}
|
||||||
</TButton>
|
</TButton>
|
||||||
))}
|
))}
|
||||||
@@ -78,6 +141,23 @@ export default function SearchPanel() {
|
|||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TPanel className={css.panel}>
|
||||||
|
<TInput
|
||||||
|
className={css.input}
|
||||||
|
autoFocus
|
||||||
|
kind={KINDS.withIcon}
|
||||||
|
icon={ICONS.search}
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
onIconClick={handleSearchSubmit}
|
||||||
|
/>
|
||||||
|
{renderContents()}
|
||||||
</TPanel>
|
</TPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user