Skip to content

Commit

Permalink
[Feature] - 데모데이 피드백 반영(리버) (#444)
Browse files Browse the repository at this point in the history
* feat(PlaceDetailCard): 여행기 사진 없을 경우 noImage이 아닌 아무것도 안나오도록 수정

* test(PlaceDetailCard): 이미지 없는 경우 테스트의 args 수정

* feat(getDaysAndNights): 여행일자 텍스트 생성 util 함수 구현

* refactor(TravelogueDetailPage,TravelPlanDetailPage): 여행 일자 계산 util 함수 사용

* feat(useInitialTripTitle): 여행기,여행계획 제목 초기값 생성 커스텀 훅 구현

* feat(TravelogueRegisterPage,useTravelPlanForm): 여행기,여행계획 변환시 제목 default값 제공

* feat(usePreviousPage): 뒤로가기를 관리하는 훅 구현

- 그 전페이지가 my 페이지이고 현재 페이지가 login 페이지면 뒤로 가기 누를시 메인 페이지로 이동하도록 구현

* chore(useInitialTripTitle): Trip을 Travel로 수정

* feat(TravelogueRegisterPage): 제목 input에 placeholder 추가

* fix(useInitialTravelTitle): 전환시에만 default title을 반환하도록 수정

* fix(interceptor): 리다이렉트 중이 아닐 때 alert,리다리렉트하도록 수정

- 인가 인증이 필요한 페이지에서 로그아웃 후 다시 인가 인증이 필요한 페이지로 이동시 로그인 하라는 alert창이 2번 뜨는 이슈가 있었는데, 리다이렉트 중에는 alert 및 리다이렉트를 하지 않도록 하여 수정함

* feat(queryKey): search 쿼리키에 searchType 추가

* feat(useInfiniteSearchTravelogue): 여행기 검색 param에 searchType 추가

* style(Tab): 텍스트 세로 가운데 정렬 및 theme 사용 리팩토링

* refactor(TravelogueList): 검색 결과 부분을 컴포넌트로 분리

* feat(SearchPage): 검색 타입 Tab 추가

* feat(getInitialTravelTitle): 제목 초기값에서 사용자 닉네임 삭제

- 사용자 닉네임을 삭제하면서 훅이 아니게되어 함수이름을 useInitialTravelTitle에서 getInitialTravelTitle로 수정하고 util 폴더로 이동 시켰습니다.

* refactor(usePreviousPage): type에서 interface로 수정

* refactor(usePreviousPage): 필요없는 state 삭제 및 그 전 페이지만 저장하도록 수정

- 그전 페이지만 알면되므로 객체로 저장할 필요가 없고, 그전 페이지를 state로 관리할 필요가 없기에 수정했습니다.
  • Loading branch information
0jenn0 authored Sep 26, 2024
1 parent cf7943c commit a78cad1
Show file tree
Hide file tree
Showing 19 changed files with 263 additions and 128 deletions.
6 changes: 4 additions & 2 deletions frontend/src/apis/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ import { HTTP_STATUS_CODE_MAP } from "@constants/httpStatusCode";
import { ROUTE_PATHS_MAP } from "@constants/route";
import { STORAGE_KEYS_MAP } from "@constants/storage";

let isRedirecting = false;

export const checkAccessToken = (
config: InternalAxiosRequestConfig,
accessToken: string | null,
) => {
if (!accessToken) {
if (!accessToken && !isRedirecting) {
isRedirecting = true;
alert(ERROR_MESSAGE_MAP.api.login);
window.location.href = ROUTE_PATHS_MAP.login;
}

return config;
};

Expand Down
8 changes: 3 additions & 5 deletions frontend/src/components/common/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useLocation, useNavigate } from "react-router-dom";

import usePreviousPage from "@hooks/usePreviousPage";
import useUser from "@hooks/useUser";

import { ROUTE_PATHS_MAP } from "@constants/route";
Expand Down Expand Up @@ -44,6 +45,7 @@ const Header = ({
};

const handleClickMyPage = () => navigate(ROUTE_PATHS_MAP.my);
const goBack = usePreviousPage();

return (
<Drawer>
Expand All @@ -52,11 +54,7 @@ const Header = ({
<IconButton
color={isLogoUsed ? theme.colors.primary : PRIMITIVE_COLORS.black}
iconType={isLogoUsed ? "korean-logo" : "back-icon"}
onClick={
isLogoUsed
? () => navigate(ROUTE_PATHS_MAP.root)
: () => navigate(ROUTE_PATHS_MAP.back)
}
onClick={isLogoUsed ? () => navigate(ROUTE_PATHS_MAP.root) : () => goBack()}
/>
</S.LeftWrapper>

Expand Down
16 changes: 11 additions & 5 deletions frontend/src/components/common/Tab/Tab.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@ export const TabList = styled.ul`
`;

export const TabItem = styled.li<{ isSelected: boolean; $tabCount: number }>`
display: flex;
flex: 0 0 calc(100% / ${({ $tabCount }) => ($tabCount <= 3 ? $tabCount : 3.5)});
padding: 1rem 2rem;
border-bottom: 2px solid ${(props) => (props.isSelected ? "#0090ff" : "transparent")};
justify-content: center;
align-items: center;
padding: ${({ theme }) => theme.spacing.s} ${({ theme }) => theme.spacing.l};
border-bottom: 2px solid
${({ isSelected, theme }) => (isSelected ? `${theme.colors.primary}` : "transparent")};
color: ${(props) => (props.isSelected ? "#0090ff" : "#616161")};
font-weight: ${(props) => (props.isSelected ? "bold" : "normal")};
font-size: 12px;
color: ${({ isSelected, theme }) =>
isSelected ? `${theme.colors.primary}` : `${theme.colors.text.secondary}`};
${({ isSelected, theme }) =>
isSelected ? theme.typography.mobile.detailBold : theme.typography.mobile.detail};
text-align: center;
white-space: nowrap;
cursor: pointer;
Expand Down
22 changes: 8 additions & 14 deletions frontend/src/components/pages/search/SearchPage.styled.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { css } from "@emotion/react";
import styled from "@emotion/styled";

import { SPACING } from "@styles/tokens";
import { PRIMITIVE_COLORS, SPACING } from "@styles/tokens";

export const Layout = styled.div`
display: flex;
flex-direction: column;
gap: ${SPACING.m};
margin-top: ${SPACING.m};
min-height: calc(100vh - 6rem);
padding: ${SPACING.m};
padding-top: 0;
`;

export const SearchFallbackWrapper = styled.div`
flex: 1;
position: relative;
`;

export const MainPageTraveloguesList = styled.ul`
Expand All @@ -25,15 +23,11 @@ export const MainPageTraveloguesList = styled.ul`
gap: ${SPACING.m};
`;

export const searchResultTextStyle = css`
display: -webkit-box;
overflow: hidden;
width: 100%;
max-width: 100%;
export const TabStyle = css`
position: fixed;
z-index: 1000;
width: 45rem;
height: 5rem;
line-height: 1.5;
white-space: normal;
word-break: break-word;
overflow-wrap: break-word;
text-overflow: ellipsis;
background-color: ${PRIMITIVE_COLORS.white};
`;
82 changes: 11 additions & 71 deletions frontend/src/components/pages/search/SearchPage.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
import { useLocation } from "react-router-dom";

import { css } from "@emotion/react";

import useInfiniteSearchTravelogues from "@queries/useInfiniteSearchTravelogues";

import { FloatingButton, SearchFallback, Text } from "@components/common";
import TravelogueCard from "@components/pages/main/TravelogueCard/TravelogueCard";

import useIntersectionObserver from "@hooks/useIntersectionObserver";

import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";
import { FloatingButton, SearchFallback, Tab } from "@components/common";

import { extractLastPath } from "@utils/extractId";

import TravelogueCardSkeleton from "../main/TravelogueCard/skeleton/TravelogueCardSkeleton";
import * as S from "./SearchPage.styled";
import TravelogueList from "./TravelogueList/TravelogueList";

const SearchPage = () => {
const SKELETON_COUNT = 5;

const location = useLocation();
const encodedKeyword =
location.pathname.split("/").length > 2 ? extractLastPath(location.pathname) : "";
const keyword = encodedKeyword ? decodeURIComponent(encodedKeyword) : "";

const { travelogues, status, fetchNextPage, isPaused, error } =
useInfiniteSearchTravelogues(keyword);

const { lastElementRef } = useIntersectionObserver(fetchNextPage);

if (!keyword) {
return (
<S.Layout>
Expand All @@ -42,63 +26,19 @@ const SearchPage = () => {
);
}

if (travelogues.length === 0 && status === "success") {
return (
<S.Layout>
{keyword && (
<Text css={S.searchResultTextStyle} textType="title">{`"${keyword}" 검색 결과`}</Text>
)}
<S.SearchFallbackWrapper>
<SearchFallback title="휑" text="검색 결과가 없어요." />
</S.SearchFallbackWrapper>
</S.Layout>
);
}

if (status === "error") {
error && alert(error.message);

return <SearchFallback title="휑" text="검색 결과가 없어요." />;
}

if (isPaused) alert(ERROR_MESSAGE_MAP.network);

return (
<S.Layout>
<FloatingButton />
{keyword && (
<Text css={S.searchResultTextStyle} textType="title">{`"${keyword}" 검색 결과`}</Text>
)}
{status === "pending" && (
<S.MainPageTraveloguesList>
{Array.from({ length: SKELETON_COUNT }, (_, index) => (
<TravelogueCardSkeleton key={index} />
))}
</S.MainPageTraveloguesList>
)}
<S.MainPageTraveloguesList>
{travelogues.map(
({ id, title, thumbnail, authorProfileUrl, likeCount, tags, authorNickname }) => (
<TravelogueCard
key={id}
travelogueOverview={{
id,
title,
thumbnail,
authorProfileUrl,
likeCount,
tags,
authorNickname,
}}
/>
),
<Tab
labels={["제목", "작성자"]}
tabContent={(selectedIndex) => (
<TravelogueList
key={`${keyword}-${selectedIndex}`}
keyword={keyword}
searchType={selectedIndex === 0 ? "TITLE" : "AUTHOR"}
/>
)}
</S.MainPageTraveloguesList>
<div
ref={lastElementRef}
css={css`
height: 1px;
`}
css={S.TabStyle}
/>
</S.Layout>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { css } from "@emotion/react";
import styled from "@emotion/styled";

import { SPACING } from "@styles/tokens";

export const Layout = styled.div`
display: flex;
flex-direction: column;
margin-top: ${SPACING.xxxl};
min-height: calc(100vh - 16rem);
`;

export const searchResultTextStyle = css`
display: -webkit-box;
overflow: hidden;
width: 100%;
max-width: 100%;
line-height: 1.5;
white-space: normal;
word-break: break-word;
overflow-wrap: break-word;
text-overflow: ellipsis;
`;

export const SearchFallbackWrapper = styled.div`
flex: 1;
position: relative;
`;

export const MainPageTraveloguesList = styled.ul`
display: flex;
flex-direction: column;
gap: ${SPACING.m};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { css } from "@emotion/react";

import type { SearchType } from "@type/domain/travelogue";

import useInfiniteSearchTravelogues from "@queries/useInfiniteSearchTravelogues";

import { SearchFallback, Text } from "@components/common";
import TravelogueCard from "@components/pages/main/TravelogueCard/TravelogueCard";
import TravelogueCardSkeleton from "@components/pages/main/TravelogueCard/skeleton/TravelogueCardSkeleton";

import useIntersectionObserver from "@hooks/useIntersectionObserver";

import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";

import * as S from "./TravelogueList.styled";

const SKELETON_COUNT = 5;

interface TravelogueListProps {
keyword: string;
searchType: SearchType;
}

const TravelogueList = ({ keyword, searchType }: TravelogueListProps) => {
const { travelogues, status, fetchNextPage, isPaused, error } = useInfiniteSearchTravelogues(
keyword,
searchType,
);
const { lastElementRef } = useIntersectionObserver(fetchNextPage);

if (travelogues.length === 0 && status === "success") {
return (
<S.Layout>
{keyword && (
<Text css={S.searchResultTextStyle} textType="title">{`"${keyword}" 검색 결과`}</Text>
)}
<S.SearchFallbackWrapper>
<SearchFallback title="휑" text="검색 결과가 없어요." />
</S.SearchFallbackWrapper>
</S.Layout>
);
}

if (status === "error") {
error && alert(error.message);

return <SearchFallback title="휑" text="검색 결과가 없어요." />;
}

if (isPaused) alert(ERROR_MESSAGE_MAP.network);

return (
<S.Layout>
<S.MainPageTraveloguesList>
{keyword && (
<Text css={S.searchResultTextStyle} textType="title">{`"${keyword}" 검색 결과`}</Text>
)}

{status === "pending" && (
<S.MainPageTraveloguesList>
{Array.from({ length: SKELETON_COUNT }, (_, index) => (
<TravelogueCardSkeleton key={index} />
))}
</S.MainPageTraveloguesList>
)}
{travelogues.map(
({ id, title, thumbnail, authorProfileUrl, likeCount, tags, authorNickname }) => (
<TravelogueCard
key={id}
travelogueOverview={{
id,
title,
thumbnail,
authorProfileUrl,
likeCount,
tags,
authorNickname,
}}
/>
),
)}
<div
ref={lastElementRef}
css={css`
height: 1px;
`}
/>
</S.MainPageTraveloguesList>
</S.Layout>
);
};

export default TravelogueList;
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ROUTE_PATHS_MAP } from "@constants/route";

import { extractLastPath } from "@utils/extractId";
import getDateRange from "@utils/getDateRange";
import getDaysAndNights from "@utils/getDaysAndNights";
import { isUUID } from "@utils/uuid";

import theme from "@styles/theme";
Expand All @@ -35,10 +36,7 @@ const TravelPlanDetailPage = () => {

const navigate = useNavigate();

const daysAndNights =
data?.days.length && data?.days.length > 1
? `${data?.days.length - 1}${data?.days.length}일`
: "당일치기";
const daysAndNights = getDaysAndNights(data?.days);

const { mutate: mutateDeleteTravelPlan, isPending: isDeletingPending } = useDeleteTravelPlan();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";
import { ROUTE_PATHS_MAP } from "@constants/route";

import { extractID } from "@utils/extractId";
import getDaysAndNights from "@utils/getDaysAndNights";

import theme from "@styles/theme";
import { SEMANTIC_COLORS } from "@styles/tokens";
Expand All @@ -49,10 +50,7 @@ const TravelogueDetailPage = () => {

const navigate = useNavigate();

const daysAndNights =
data?.days.length && data?.days.length > 1
? `${data?.days.length - 1}${data?.days.length}일`
: "당일치기";
const daysAndNights = getDaysAndNights(data?.days);

const { onTransformTravelDetail } = useTravelTransformDetailContext();
const {
Expand Down
Loading

0 comments on commit a78cad1

Please sign in to comment.