Skip to content

Commit

Permalink
위키-술-검색기능-추가 (#58)
Browse files Browse the repository at this point in the history
* Minor : 스타일링 체인지

* Minor : 사용하지 않는 모듈제거, 포매팅

* Refactor : 술 리스트 컴포넌트, 페이지네이션 분리

* Refactor : 술 리스트 컴포넌트, 페이지네이션 분리

* Refactor : 키워드가 입력되지 않았을 경우 처리

* New : 로컬스토리지 키를 상수로 관리

* New : UX개선을 위한 스켈레톤 표시 딜레이 타이머 추가

* New : 술 검색기능 구현
  • Loading branch information
jobkaeHenry authored Nov 28, 2023
1 parent 8de995b commit 7ea46c0
Show file tree
Hide file tree
Showing 16 changed files with 347 additions and 72 deletions.
42 changes: 25 additions & 17 deletions client/src/app/wiki/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
"use client";
import { Paper, Container } from "@mui/material";
import WikiAppbar from "@/components/wiki/WikiAppbar";
import { ReactNode } from "react";
import { ReactNode, useState } from "react";
import WikiPageContext from "@/store/wiki/WikiPageContext";
import WikiSearchDrawer from "@/components/wiki/searchDrawer/WikiSearchDrawer";

const layout = ({ children }: { children: ReactNode }) => {
const [isSearching, setIsSearching] = useState(false);

return (
<Paper>
<WikiAppbar />
<Container sx={{ p: { xs: 0, sm: 4 } }} maxWidth={"lg"}>
<Paper
sx={{
display: "flex",
position: "relative",
flexDirection: "column",
gap: 2,
p: 2,
}}
>
{children}
</Paper>
</Container>
</Paper>
<WikiPageContext.Provider value={{ isSearching, setIsSearching }}>
<Paper>
<WikiAppbar />
<Container sx={{ p: { xs: 0, sm: 4 } }} maxWidth={"lg"}>
<Paper
sx={{
display: "flex",
position: "relative",
flexDirection: "column",
gap: 2,
p: 2,
}}
>
<WikiSearchDrawer />
{children}
</Paper>
</Container>
</Paper>
</WikiPageContext.Provider>
);
};

Expand Down
4 changes: 2 additions & 2 deletions client/src/app/wiki/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AlcoholList from "@/components/wiki/AlcoholList";
import AlcoholPagination from "@/components/wiki/AlcoholPagination";
import WikiAlcoholSelector from "@/components/wiki/WikiAlcoholSelector";
import { Stack } from "@mui/material";
import SectionHeading from "@/components/SectionHeading";
Expand All @@ -14,7 +14,7 @@ const WikiPage = async () => {
subTitle={"투파이아들이 쓴 리뷰를 확인할 수 있어요!"}
/>
<WikiAlcoholSelector />
<AlcoholList />
<AlcoholPagination />

<SectionHeading title={"술 정보"} subTitle={"곧 출시 됩니다!"} />
<Stack alignItems="center">
Expand Down
72 changes: 72 additions & 0 deletions client/src/components/SearchHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Button, Stack, StackProps, Typography } from "@mui/material";
import { useCallback, useState } from "react";
import XIcon from "@/assets/icons/XIcon.svg";

interface SearchHistoryProps extends Omit<StackProps, "onClick"> {
storageKey: string;
onClick: () => void;
}

const SearchHistory = ({ storageKey, onClick }: SearchHistoryProps) => {
const getItems = useCallback(() => {
return JSON.parse(localStorage.getItem(storageKey) ?? "[]") as string[];
}, [storageKey]);

const [searchHistory, setSearchHistory] = useState<string[]>(getItems());

const removeAll = useCallback(() => {
localStorage.setItem(storageKey, "[]");
setSearchHistory(getItems());
}, [storageKey]);

const removeByKeyword = useCallback(
(keyword: string) => {
const filteredHistory = searchHistory.filter(
(prevKeyword) => prevKeyword !== keyword
);
localStorage.setItem(storageKey, JSON.stringify(filteredHistory));
setSearchHistory(getItems());
},
[storageKey]
);

return searchHistory.length > 0 ? (
<>
<Stack direction="row" justifyContent="space-between">
<Typography variant="subtitle1" fontWeight="bold">
최근 검색어
</Typography>
<Button onClick={removeAll} variant="text" sx={{ fontWeight: "bold" }}>
전체 삭제
</Button>
</Stack>
<Stack>
{searchHistory.map((keyword) => (
<Stack
key={keyword}
onClick={onClick}
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Typography variant="subtitle2">{keyword}</Typography>
<Button
onClick={(e) => {
e.stopPropagation();
removeByKeyword(keyword);
}}
variant="text"
sx={{ justifyContent: "end" }}
>
<XIcon />
</Button>
</Stack>
))}
</Stack>
</>
) : (
<></>
);
};

export default SearchHistory;
8 changes: 5 additions & 3 deletions client/src/components/newpost/SearchAlcoholInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const SearchAlcoholInput = ({ setAlcoholNo }: SearchAlcoholInputInterface) => {
const [selectedAlcohol, setSelectedAlcohol] =
useState<AlcoholDetailInterface>();


useEffect(() => {
setSearchKeyword(selectedAlcohol?.alcoholName ?? "");
setAlcoholNo(selectedAlcohol?.alcoholNo);
Expand All @@ -48,6 +47,9 @@ const SearchAlcoholInput = ({ setAlcoholNo }: SearchAlcoholInputInterface) => {
name="positionInfo"
size="small"
InputProps={{
sx: {
borderRadius: 12,
},
startAdornment: (
<InputAdornment position="start">
<AlcholeSearchIcon />
Expand Down Expand Up @@ -109,8 +111,8 @@ const SearchAlcoholInput = ({ setAlcoholNo }: SearchAlcoholInputInterface) => {
const WrapperStyle = {
width: "calc(100% - 32px)",
minHeight: "50px",
maxHeight:'142px',
overflowY:'auto',
maxHeight: "142px",
overflowY: "auto",
backgroundColor: "#F5F5F5",
border: "1px solid #E6E6E6",
borderRadius: 1.5,
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/search/SearchArea.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import { Box, CircularProgress, Paper, TextField } from "@mui/material";
import React, { useState, useMemo, Suspense } from "react";
import { Paper, TextField } from "@mui/material";
import React, { useState, useMemo } from "react";
import PostCardList from "@/components/post/PostCardList";
import { AugmentedGetPostListResponse } from "@/queries/post/useGetPostListInfiniteQuery";
import useDebounce from "@/hooks/useDebounce";
Expand Down
53 changes: 21 additions & 32 deletions client/src/components/wiki/AlcoholList.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
"use client";
import AlcoholNameTag from "@/components/wiki/AlcoholNameTag";
import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery";
import { Box, Pagination, Skeleton, Stack } from "@mui/material";
import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface";
import { Typography } from "@mui/material";
import { memo } from "react";

const AlcoholList = () => {
const { data: alcohols } = useGetAlcoholListQuery();
const AlcoholList = ({
data: alcohols,
}: {
data: AlcoholDetailInterface[];
}) => {
return (
<Stack alignItems="center" gap={2}>
<Stack gap={1} alignItems="center" width={"100%"} height={"232px"}>
{alcohols ? (
alcohols.list.map((alcohol) => (
<AlcoholNameTag
key={alcohol.alcoholNo}
alcoholName={alcohol.alcoholName}
alcoholType={alcohol.alcoholType}
/>
))
) : (
<AlcoholListSkeleton />
)}
</Stack>
<Pagination count={alcohols?.totalCount} />
</Stack>
<>
{alcohols?.length > 0 ? (
alcohols.map((alcohol) => (
<AlcoholNameTag
key={alcohol.alcoholNo}
alcoholName={alcohol.alcoholName}
alcoholType={alcohol.alcoholType}
/>
))
) : (
<Typography textAlign="center">검색 결과가 없어요</Typography>
)}
</>
);
};
export default memo(AlcoholList);

const AlcoholListSkeleton = memo(() => {
return Array.from(new Array(5)).map(() => (
<Skeleton
variant="rectangular"
width={"100%"}
height={40}
sx={{ borderRadius: 2 }}
/>
));
});

export default AlcoholList;
31 changes: 31 additions & 0 deletions client/src/components/wiki/AlcoholListSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { memo } from "react";
import { Skeleton } from "@mui/material";

import useSkeletonTimer from "@/hooks/useSkeletonTimer";

interface AlcoholListSkeletonInterface {
size?: number;
disableTimer?: boolean;
}

const AlcoholListSkeleton = memo(
({ size = 5, disableTimer }: AlcoholListSkeletonInterface) => {
const isOver200ms = !!disableTimer ? true : useSkeletonTimer();

return isOver200ms ? (
Array.from(new Array(size)).map((_e, i) => (
<Skeleton
key={i}
variant="rectangular"
width={"100%"}
height={40}
sx={{ borderRadius: 2 }}
/>
))
) : (
<></>
);
}
);

export default AlcoholListSkeleton;
24 changes: 24 additions & 0 deletions client/src/components/wiki/AlcoholPagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";
import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery";
import AlcoholList from "@/components/wiki/AlcoholList";
import { Pagination, Stack } from "@mui/material";
import AlcoholListSkeleton from "@/components/wiki/AlcoholListSkeleton";

const AlcoholPagenation = () => {
const { data: alcohols, isSuccess } = useGetAlcoholListQuery();

return (
<Stack alignItems="center" gap={2}>
<Stack gap={1} alignItems="center" width={"100%"} height={"232px"}>
{isSuccess ? (
<AlcoholList data={alcohols?.list} />
) : (
<AlcoholListSkeleton disableTimer />
)}
</Stack>
<Pagination count={alcohols?.totalCount} />
</Stack>
);
};

export default AlcoholPagenation;
28 changes: 15 additions & 13 deletions client/src/components/wiki/WikiAlcoholSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,30 @@ import TraditionalAlcoholIcon from "@/assets/icons/Alcohol/TraditionalAlcoholIco
import SakeIcon from "@/assets/icons/Alcohol/SakeIcon.svg";

const WikiAlcoholSelector = () => {

const btnList =useMemo(()=>[
{ title: "포도주", iconComponent: <WineIcon /> },
{ title: "위스키", iconComponent: <WiskyIcon /> },
{ title: "증류주", iconComponent: <SpiritsIcon /> },
{ title: "우리술", iconComponent: <TraditionalAlcoholIcon /> },
{ title: "사케", iconComponent: <SakeIcon /> },
],[])
const btnList = useMemo(
() => [
{ title: "포도주", iconComponent: <WineIcon /> },
{ title: "위스키", iconComponent: <WiskyIcon /> },
{ title: "증류주", iconComponent: <SpiritsIcon /> },
{ title: "우리술", iconComponent: <TraditionalAlcoholIcon /> },
{ title: "사케", iconComponent: <SakeIcon /> },
],
[]
);

const [selectedAlcohol, setSelectedAlcohol] = useState(btnList[0].title);

const clickHandler = useCallback((title:string)=>{
setSelectedAlcohol(title)
},[])
const clickHandler = useCallback((title: string) => {
setSelectedAlcohol(title);
}, []);

return (
<Stack direction="row" justifyContent='center' gap={2}>
<Stack direction="row" justifyContent="center" gap={2}>
{btnList.map((btnInfo) => (
<WikiAlcoholSelectorBtn
key={btnInfo.title}
isSelected={selectedAlcohol === btnInfo.title}
onClick={()=>clickHandler(btnInfo.title)}
onClick={() => clickHandler(btnInfo.title)}
{...btnInfo}
/>
))}
Expand Down
9 changes: 6 additions & 3 deletions client/src/components/wiki/WikiAppbar.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
'use client'
"use client";
import CustomAppbar from "@/components/CustomAppbar";
import SearchIcon from "@/assets/icons/SearchIcon.svg";
import { memo, useContext } from "react";
import WikiPageContext from "@/store/wiki/WikiPageContext";

const WikiAppbar = () => {
const { setIsSearching } = useContext(WikiPageContext);
return (
<CustomAppbar
title="술백과"
buttonComponent={<SearchIcon />}
onClickButton={() => console.log("눌림")}
onClickButton={() => setIsSearching(true)}
/>
);
};

export default WikiAppbar;
export default memo(WikiAppbar);
Loading

0 comments on commit 7ea46c0

Please sign in to comment.