From c22a61c78209db5b58ec6702a689b467df4591ce Mon Sep 17 00:00:00 2001 From: readygetset Date: Tue, 16 Jan 2024 01:02:41 +0900 Subject: [PATCH 1/3] feat: clothes introduction page --- src/components/StatusSign/index.tsx | 73 +++++++++++++++++++++++ src/data/messages.ts | 3 + src/hooks/api/clothes/clothes.ts | 49 ++++++++++++++++ src/pages/Clothes/index.tsx | 90 +++++++++++++++++++++++++++++ src/route/index.tsx | 2 + 5 files changed, 217 insertions(+) create mode 100644 src/components/StatusSign/index.tsx create mode 100644 src/hooks/api/clothes/clothes.ts create mode 100644 src/pages/Clothes/index.tsx diff --git a/src/components/StatusSign/index.tsx b/src/components/StatusSign/index.tsx new file mode 100644 index 0000000..9a9d113 --- /dev/null +++ b/src/components/StatusSign/index.tsx @@ -0,0 +1,73 @@ +import { Box, Typography } from '@mui/material'; + +interface StatusSignProp { + status: string; +} +export default function StatusSign({ status }: StatusSignProp) { + if (status === '대여가능') { + return ( + + + {status} + + ); + } + if (status === '대여불가') { + return ( + + + {status} + + ); + } + if (status === '대여중') { + return ( + + + {status} + + ); + } + if (status === '공개중') { + return ( + + + {status} + + ); + } + + return null; +} diff --git a/src/data/messages.ts b/src/data/messages.ts index 793b48f..f4bb64e 100644 --- a/src/data/messages.ts +++ b/src/data/messages.ts @@ -1,3 +1,6 @@ export const LOGIN_MESSAGE = { LOGIN_FAIL: '로그인에 실패하였습니다 :(', }; +export const CLOTHES_MESSAGE = { + CLOTHES_NOT_FOUND: '옷을 찾을 수 없어요 :(', +}; diff --git a/src/hooks/api/clothes/clothes.ts b/src/hooks/api/clothes/clothes.ts new file mode 100644 index 0000000..a69d92e --- /dev/null +++ b/src/hooks/api/clothes/clothes.ts @@ -0,0 +1,49 @@ +import { enqueueSnackbar } from 'notistack'; +import axios, { AxiosError } from 'axios'; + +import { CLOTHES_MESSAGE } from '../../../data/messages'; + +interface Reviewer { + id?: number; + username: string; + nickname?: string; +} +interface Review { + review: string; + reviewer: Reviewer; +} +interface Owner { + id?: number; + nickname?: string; + location?: string; +} +export interface GetClothesResponse { + id: number; + description: string; + category: string; + season: string; + status: string; + isOpen: boolean; + name: string; + tag: string; + image: string; + owner: Owner; + review: Review[]; + isWished: boolean; +} + +export async function getClothesAPICall(clothesId: number) { + try { + const response = await axios.get(`${process.env.REACT_APP_API_URL}/clothes/${clothesId}`); + if (response.status === 200) { + return response.data; + } + } catch (err) { + if (err instanceof AxiosError) { + enqueueSnackbar(err.response?.data?.message ?? CLOTHES_MESSAGE.CLOTHES_NOT_FOUND, { variant: 'error' }); + } else { + enqueueSnackbar(CLOTHES_MESSAGE.CLOTHES_NOT_FOUND, { variant: 'error' }); + } + } + return null; +} diff --git a/src/pages/Clothes/index.tsx b/src/pages/Clothes/index.tsx new file mode 100644 index 0000000..3916d90 --- /dev/null +++ b/src/pages/Clothes/index.tsx @@ -0,0 +1,90 @@ +import { useParams } from 'react-router-dom'; +import { useEffect, useState } from 'react'; +import { Box, Card, Chip, Divider, Typography } from '@mui/material'; +import LocationOnIcon from '@mui/icons-material/LocationOn'; + +import { GetClothesResponse, getClothesAPICall } from '../../hooks/api/clothes/clothes'; +import StatusSign from '../../components/StatusSign'; + +// TODO: 사용자 페이지 링크 추가 +export function ClothesPage() { + const { id } = useParams(); + const clothesId = Number(id); + const [clothes, setClothes] = useState(null); + const getClothes = async () => { + try { + const result = await getClothesAPICall(clothesId); + setClothes(result); + } catch (error) { + console.error(error); + } + }; + useEffect(() => { + getClothes(); + }, [clothesId]); + + return ( + + + + {clothes?.image ? ( + + ) : ( + + )} + + + + + + + + {clothes?.name} + + {clothes?.category} + + + + {clothes?.owner.nickname}님 + + + {clothes?.owner.location} + + + + + 상품정보 + + + {clothes?.description} + + + + 리뷰 + + + {clothes?.review.map((review) => ( + + {review.reviewer.nickname} | {review.review} + + ))} + + + + + ); +} diff --git a/src/route/index.tsx b/src/route/index.tsx index 178a20e..182716a 100644 --- a/src/route/index.tsx +++ b/src/route/index.tsx @@ -4,6 +4,7 @@ import { RegisterPage } from '../pages/Register'; import { MainPage } from '../pages/Main'; import { LoginPage } from '../pages/Login'; import { ListPage } from '../pages/List'; +import { ClothesPage } from '../pages/Clothes'; /** * 어느 url에 어떤 페이지를 보여줄지 정해주는 컴포넌트입니다. @@ -16,6 +17,7 @@ export function RouteComponent() { } /> } /> } /> + } /> ); } From f4904cadc253e127e5cbeaa68b271d78884ee6f8 Mon Sep 17 00:00:00 2001 From: readygetset Date: Tue, 16 Jan 2024 17:22:00 +0900 Subject: [PATCH 2/3] feat: clothes intro page completion --- src/components/ApplyBtn/index.tsx | 27 +++ src/components/Header/index.tsx | 6 +- src/components/StatusSign/index.tsx | 10 +- src/components/WishBtn/index.tsx | 18 ++ src/data/messages.ts | 7 + src/hooks/api/auth/login.ts | 2 + src/hooks/api/clothes/clothes.ts | 56 +++++- src/hooks/api/wish/wish.ts | 55 ++++++ src/models/enum.ts | 1 + src/pages/Clothes/index.tsx | 255 +++++++++++++++++++++------- src/pages/Login/index.tsx | 11 +- src/recoil/atom.ts | 8 - 12 files changed, 368 insertions(+), 88 deletions(-) create mode 100644 src/components/ApplyBtn/index.tsx create mode 100644 src/components/WishBtn/index.tsx create mode 100644 src/hooks/api/wish/wish.ts create mode 100644 src/models/enum.ts delete mode 100644 src/recoil/atom.ts diff --git a/src/components/ApplyBtn/index.tsx b/src/components/ApplyBtn/index.tsx new file mode 100644 index 0000000..aade53e --- /dev/null +++ b/src/components/ApplyBtn/index.tsx @@ -0,0 +1,27 @@ +import { Button } from '@mui/material'; + +interface ApplyBtnProp { + status: string; +} +export default function ApplyBtn({ status }: ApplyBtnProp) { + if (status === '대여가능') { + return ( + + ); + } + if (status === '대여불가능') { + return ( + + ); + } + if (status === '대여중') { + return ( + + ); + } + return null; +} diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 34c9f96..ff63a01 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,19 +1,17 @@ -import { useRecoilValue } from 'recoil'; import { Link, useNavigate } from 'react-router-dom'; import { useState } from 'react'; import { Typography, Box, Button } from '@mui/material'; import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import ConfirmDialog from '../ConfirmDialog'; -import { userAtom } from '../../recoil/atom'; export default function Header() { const navigate = useNavigate(); const isAuthenticated = sessionStorage.getItem('accessToken'); + const userNickname = sessionStorage.getItem('userNickname'); const [isOpen, setIsOpen] = useState(false); - const userState = useRecoilValue(userAtom); const handleLogout = (e: React.MouseEvent) => { e.preventDefault(); sessionStorage.removeItem('accessToken'); @@ -48,7 +46,7 @@ export default function Header() { {isAuthenticated ? ( - {userState.nickname} 님 + {userNickname} 님