Skip to content

Commit

Permalink
Feat: 검색 페이지 퍼블리싱 (#29)
Browse files Browse the repository at this point in the history
* feat: 검색바 퍼블리싱

* feat: 최근 검색어 퍼블리싱

* feat: 인기있는 검색어 퍼블리싱

* feat: 연관 검색어 퍼블리싱

* feat: 검색 결과 페이지 퍼블리싱

* feat: 검색 가이드 퍼블리싱

* feat: search ->  result 화면 이동 연결

* chore: 폴더구조 변경

* chore: 폴더구조 변경

* feat: 하루동안 보지 않기 구현

* feat: 최근 검색어 localstorage 조작 로직 구현

* feat: 하루동안 보지 않기 재구현 cookie -> localstorage

* feat: 장소 카드 공통 컴포넌트로 분리

* chore: 공백 제거

* chore: 오타 수정

* fix: 검색 path 조작 오류 수정

* review: 디자인 오류 수정
  • Loading branch information
seobbang committed Sep 7, 2024
1 parent 05b3926 commit 326564c
Show file tree
Hide file tree
Showing 22 changed files with 771 additions and 3 deletions.
11 changes: 11 additions & 0 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import DetailPage from './views/Detail/pages/DetailPage';
import LoginCallBack from './views/Login/components/LoginCallBack';
import MainPage from './views/Main/pages/MainPage';
import Mypage from './views/Mypage/pages/Mypage';
import SearchPage from './views/Search/pages/SearchPage';
import SearchResultPage from './views/Search/pages/SearchResultPage';

const router = createBrowserRouter([
{
Expand All @@ -16,6 +18,15 @@ const router = createBrowserRouter([
],
},
{ path: '/detail', element: <DetailPage /> },
{
path: '/search',
element: <SearchPage />,
children: [{}],
},
{
path: '/search/:word',
element: <SearchResultPage />,
},
{
path: '/mypage',
element: <Mypage />,
Expand Down
3 changes: 3 additions & 0 deletions src/assets/icon/Chevron_Left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/assets/icon/icon-check-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions src/assets/icon/icon-search-set.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/assets/icon/icon-x-mono.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion src/assets/icon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export { default as MapPinIcon } from './icon_map_pin.svg?react';
export { default as StarIcon } from './icon_star_filled.svg?react';
export { default as Star1Icon } from './Star 1.svg?react';
export { default as UnitripIcon } from './UNITRIP.svg?react';

export { default as ArrowToggleClosed } from './icon_arrow_toggle_closed.svg?react';
export { default as ArrowToggleOpen } from './icon_arrow_toggle_open.svg?react';
export { default as AudioGuideDefaultIcon } from './icon_audio_guide_default.svg?react';
Expand Down Expand Up @@ -70,6 +69,11 @@ export { default as VideoGuideSubtitleDefaultIcon } from './icon_vedio_guide_sub
export { default as VideoGuideSubtitleNoneIcon } from './icon_video_guide_subtitle_none.svg?react';
export { default as WheelchairTicketOfficeDefaultIcon } from './icon_wheelchair_ticket_office_default.svg?react';
export { default as WheelchairTicketOfficeNoneIcon } from './icon_wheelchair_ticket_office_none.svg?react';
export { default as ChevronLeftIcon } from './Chevron_Left.svg?react';
export { default as ToggleXIcon } from './toggle-x.svg?react';
export { default as ResetXIcon } from './reset-x.svg?react';
export { default as SearchSetIcon } from './icon-search-set.svg?react';
export { default as CheckFillIcon } from './icon-check-fill.svg?react';

export { default as ArrowRightIcon } from './icon-arrow-right.svg?react';
export { default as HeaderBackIcon } from './icon_header_back.svg?react';
Expand Down
5 changes: 5 additions & 0 deletions src/assets/icon/reset-x.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/icon/toggle-x.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,6 @@ const closeButtonCss = css`
position: absolute;
top: 2rem;
right: 2rem;
color: ${COLORS.gray5};
`;
3 changes: 2 additions & 1 deletion src/components/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ const PATH_MATCH = [

const MenuBar = () => {
const { pathname } = useLocation();
const firstPathname = `/${pathname.split('/')[1]}`;

const menuList = PATH_MATCH.map(({ url, name, icon }) => {
return (
<Link
key={url}
to={url}
className={pathname === url ? 'selected' : ''}
className={firstPathname === url ? 'selected' : ''}
css={linkCss}>
{icon}
<span css={spanCss}>{name}</span>
Expand Down
73 changes: 73 additions & 0 deletions src/components/PlaceCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { css } from '@emotion/react';
import { Link } from 'react-router-dom';

import { HeartMonoIcon, PinLocationMonoIcon } from '@/assets/icon';
import { COLORS, FONTS } from '@/styles/constants';

interface PlaceCardProps {
placeName: string;
address: string;
}

/**
* @param placeName 장소 이름
* @param address 주소
*/

const PlaceCard = (props: PlaceCardProps) => {
const { placeName, address } = props;
return (
<Link to="" css={cardContainerCss}>
<button type="button">
<HeartMonoIcon css={iconCss} />
</button>
<p css={titleCss}>{placeName}</p>
<p css={addressCss}>
<PinLocationMonoIcon /> <span>{address}</span>
</p>
</Link>
);
};

export default PlaceCard;

const cardContainerCss = css`
display: flex;
flex-direction: column;
position: relative;
width: 100%;
height: 16.8rem;
border-radius: 1.2rem;
background-color: gray;
color: ${COLORS.white};
`;

const titleCss = css`
margin: 9.4rem 0 0 1.6rem;
${FONTS.H3};
text-align: left;
`;

const addressCss = css`
display: flex;
gap: 0.5rem;
align-items: center;
margin-left: 1.6rem;
${FONTS.Small1};
& > span {
padding-top: 0.1rem;
}
`;

const iconCss = css`
position: absolute;
top: 1.6rem;
right: 1.6rem;
`;
39 changes: 39 additions & 0 deletions src/utils/storageHideGuide.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { STORAGE_KEY } from '@/views/Search/constants/localStorageKey';

const key = STORAGE_KEY.hideSearchGuide;

const getStorageHideGuide = () => {
const hideGuideTime = localStorage.getItem(key);
return hideGuideTime ? Number(hideGuideTime) : null;
};

// 24시간을 ms로
const EXPIRATION_PERIOD = 24 * 60 * 60 * 1000;
export const setStorageHideGuide = () => {
const date = new Date();
const expirationTime = date.getTime() + EXPIRATION_PERIOD;
localStorage.setItem(key, String(expirationTime));
};

const removeStorageHideGuide = () => {
localStorage.removeItem(key);
};

export const isGuideShown = () => {
const hideGuideTime = getStorageHideGuide();
const nowDate = new Date();

if (hideGuideTime) {
// 만료 X
if (nowDate.getTime() <= hideGuideTime) {
return false;
}
// 만료 O
else {
removeStorageHideGuide();
return true;
}
}
// 만료 O
else return true;
};
36 changes: 36 additions & 0 deletions src/utils/storageSearchWord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { STORAGE_KEY } from '@/views/Search/constants/localStorageKey';

const key = STORAGE_KEY.recentSearch;
const LIST_MAX_LENGTH = 10;

export const getStorageSearchWord = (): string[] => {
return JSON.parse(localStorage.getItem(key) || '[]');
};

export const setStorageSearchWord = (newValue: string) => {
const previousList: string[] = JSON.parse(localStorage.getItem(key) || '[]');

// 이미 존재하는지 확인
const previousIndex = previousList.findIndex((item) => item === newValue);

// 존재하면 삭제
if (previousIndex !== -1) previousList.splice(previousIndex, 1);
// 존재하지 않으면 max length 확인
else {
if (previousList.length === LIST_MAX_LENGTH) previousList.pop();
}

// 새로운 값 추가
localStorage.setItem(key, JSON.stringify([newValue, ...previousList]));
};

export const removeStorageSearchWord = (newValue: string): string[] => {
const list: string[] = JSON.parse(localStorage.getItem(key) || '[]');

// 인덱스 찾아서 제거
const previousIndex = list.findIndex((item) => item === newValue);
list.splice(previousIndex, 1);
localStorage.setItem(key, JSON.stringify([...list]));

return list;
};
56 changes: 56 additions & 0 deletions src/views/Search/components/RelatedWordList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { css } from '@emotion/react';

import { SearchMonoIcon } from '@/assets/icon';
import { COLORS, FONTS } from '@/styles/constants';

interface RelatedWordListProps {
searchWord: string;
}

const RelatedWordList = (props: RelatedWordListProps) => {
const { searchWord } = props;
console.log(searchWord);

return (
<ul css={containerCss}>
<li>
<button css={wordCss}>
<SearchMonoIcon />
<span css={wordTextCss}>대전시립미술관</span>
</button>
</li>
<li>
<button css={wordCss}>
<SearchMonoIcon />
<span css={wordTextCss}>이응노미술관</span>
</button>
</li>
</ul>
);
};

export default RelatedWordList;

const containerCss = css`
display: flex;
flex-direction: column;
margin-top: 2.4rem;
`;

const wordCss = css`
display: flex;
gap: 2.2rem;
align-items: center;
height: 5.9rem;
margin-left: 2.4rem;
`;

const wordTextCss = css`
padding-top: 0.2rem;
color: ${COLORS.brand1};
${FONTS.Body3};
`;
Loading

0 comments on commit 326564c

Please sign in to comment.