From bdc10092866aa0934ac2e1cfc37744ca4d19ed41 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 19:59:35 +0900 Subject: [PATCH 01/11] =?UTF-8?q?Refactor:=20mutationKey=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EA=B0=9C=EC=84=A0=20(successMutationKey=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/mutationKey.ts | 129 +++++++++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 20 deletions(-) diff --git a/src/constants/mutationKey.ts b/src/constants/mutationKey.ts index 5cdc8d3..49e6558 100644 --- a/src/constants/mutationKey.ts +++ b/src/constants/mutationKey.ts @@ -1,17 +1,41 @@ import { postAuthRegister } from '@/apis/register/postAuthRegister'; import { postAuctionRegister } from '@/apis/auctionRegister/postAuctionRegister'; import { postArtworkRegister } from '@/apis/artworkRegister/postArtworkRegister'; -import { getAvailableArtworksQuery } from '@/constants/queryKeys'; +import { + getArtworkDetailQuery, + getAuctionListQuery, + getBuyerMypageQuery, + getExhibitionListQuery, +} from '@/constants/queryKeys'; import { postAuctionBid } from '@/apis/auction/postAuctionBid'; -import { toggleArtworkLike } from '@/apis/artwork-like/like'; -import { postMySpace } from '@/apis/artwork-detail/postMySpace'; +import { postAuctionLike } from '@/apis/auction/postAuctionLike'; +import { postAuctionUnlike } from '@/apis/auction/postAuctionUnlike'; +import { toggleArtworkLike } from '@/apis/artworkLike/like'; +import { postMySpace } from '@/apis/artworkDetail/postMySpace'; +import { getMainDataQuery } from '@/constants/queryKeys'; +import { requestKakaoPay } from '@/apis/kakaoPay/paymentPreparation'; +import { approveKakaoPay } from '@/apis/kakaoPay/paymentAuthorization'; +import { updateUserInfo } from '@/apis/mypageBuyer/artBuyer'; +import { TUpdateUserInfo } from '@/apis/mypageBuyer/type'; +import { postExhibitionRegister } from '@/apis/exhibitRegister/postExhibitRegister'; +import { TAuctionRegisterFormData } from '@/apis/auctionRegister/type'; +/** + * 뮤테이션 키 관리를 위한 상수 정의 - 상수값을 사용해 mutation 성공 여부, 실패등에 따른 값 선언을 위한 유지보수성을 높이기 위해 사용합니다. (param은 넣지 않습니다. 하위 함수에서 넣어줍니다.) + * @mutationKey - 뮤테이션 함수 구분을 위한 키 값입니다. + * @successMutationKey - 뮤테이션 성공 후 호출될 쿼리 키 값 (쿼리 키 무효화를 통해 refetch를 위해 사용할 값입니다.) + * @mutationFn - 뮤테이션 함수입니다. + * @author 홍규진 + */ +/*-----------------------------------------------------------*/ + /** * 인증 회원가입 뮤테이션 * @author 홍규진 * */ export const postAuthRegisterMutation = () => { return { - mutationKey: ['auth'], + mutationKey: ['postAuthRegister'], + successMutationKey: [...getMainDataQuery().queryKey], mutationFn: postAuthRegister, }; }; @@ -22,72 +46,137 @@ export const postAuthRegisterMutation = () => { * */ export const postAuctionRegisterMutation = () => { return { - mutationKey: [...getAvailableArtworksQuery().queryKey], - mutationFn: postAuctionRegister, + mutationKey: ['postAuctionRegister'], + successMutationKey: [...getAuctionListQuery('').queryKey], + mutationFn: (data: TAuctionRegisterFormData) => postAuctionRegister(data), + }; +}; +/** + * 전시 등록 뮤테이션 + * 전시 등록후 전시 목록 조회 쿼리 키 무효화 + * @author 홍규진 + * */ +export const postExhibitionRegisterMutation = () => { + return { + mutationKey: ['postExhibitionRegister'], + successMutationKey: [...getExhibitionListQuery('').queryKey], + mutationFn: postExhibitionRegister, }; }; /** * 작품 등록 뮤테이션 - * TODO-[홍규진] 작품 등록 후, 홈 화면 내 작품 쿼리 키 지정시 해당 키로 수정 필요 + *작품 등록 후, 메인 화면 내 작품 쿼리 키 무효화 * @author 홍규진 * */ export const postArtworkRegisterMutation = () => { return { - mutationKey: ['artwork'], + mutationKey: ['postArtworkRegister'], + successMutationKey: [...getMainDataQuery().queryKey], mutationFn: postArtworkRegister, }; }; /** * 경매 입찰 뮤테이션 - * @author 이하늘 + * 경매 입찰 후 경매 목록 조회 쿼리 키 무효화 + * @author 이하늘, 홍규진 * */ export const postAuctionBidMutation = () => { return { - mutationKey: ['auctionBid'], + mutationKey: ['postAuctionBid'], + successMutationKey: [...getAuctionListQuery('').queryKey], mutationFn: postAuctionBid, }; }; -/* +/** * 작품 좋아요 뮤테이션 - * @author 김서윤 - */ + * 작품 좋아요 후 작품 상세 조회 쿼리 키 무효화 + * @author 김서윤, 홍규진 + * */ export const toggleArtworkLikeMutation = (artworkId: number) => { return { - mutationKey: ['artworkDetail', artworkId], + mutationKey: ['toggleArtworkLike'], + successMutationKey: [...getArtworkDetailQuery(artworkId).queryKey], mutationFn: () => toggleArtworkLike(artworkId), }; }; +/** + * 카카오페이 결제 준비 뮤테이션 + * @author 노찬영, 홍규진 + * */ +export const requestKakaoPayMutation = () => { + return { + mutationKey: ['requestKakaoPay'], + successMutationKey: [...getAuctionListQuery('').queryKey], + mutationFn: (paymentId: number) => requestKakaoPay(paymentId), + }; +}; + +/** + * 카카오페이 결제 승인 뮤테이션 + * 카카오페이 결제 승인 후 경매 목록 조회 쿼리 키 무효화 + * @author 노찬영, 홍규진 + */ +export const approveKakaoPayMutation = () => { + return { + mutationKey: ['approveKakaoPay'], + successMutationKey: [...getAuctionListQuery('').queryKey], + mutationFn: (paymentId: number, pgToken: string) => + approveKakaoPay(paymentId, pgToken), + }; +}; + +/** + * 사용자 정보 수정 뮤테이션 + * 사용자 정보 수정 후 사용자 마이페이지 조회 쿼리 키 무효화 + * @author 노찬영, 홍규진 + */ +export const updateUserInfoMutation = () => { + return { + mutationKey: ['updateUserInfo'], + successMutationKey: [...getBuyerMypageQuery().queryKey], + mutationFn: (userInfo: TUpdateUserInfo) => updateUserInfo(userInfo), + }; +}; + /** * 내 공간 등록 뮤테이션 - * @author 김서윤 + * 내 공간 등록 후 사용자 마이페이지 조회 쿼리 키 무효화 + * @author 김서윤, 홍규진 * */ export const postMySpaceMutation = () => { return { - mutationKey: ['new_userspace'], + mutationKey: ['postMySpace'], + successMutationKey: [...getBuyerMypageQuery().queryKey], mutationFn: postMySpace, }; }; /** * 경매 좋아요 뮤테이션 - * @author 이하늘 + * 경매 좋아요 후 경매 목록 조회 쿼리 키 무효화 + * @author 이하늘, 홍규진 * */ export const postAuctionLikeMutation = () => { return { - mutationKey: ['auctionLike'], + mutationKey: ['postAuctionLike'], + successMutationKey: [...getAuctionListQuery('').queryKey], + mutationFn: postAuctionLike, }; }; /** * 경매 좋아요 취소 뮤테이션 - * @author 이하늘 + * 경매 좋아요 취소 후 경매 목록 조회 쿼리 키 무효화 + * @author 이하늘, 홍규진 * */ export const postAuctionUnlikeMutation = () => { return { - mutationKey: ['auctionUnlike'], + mutationKey: ['postAuctionUnlike'], + successMutationKey: [...getAuctionListQuery('').queryKey], + mutationFn: postAuctionUnlike, }; }; From 267e53b8f7ac8131d7ddf3c8a39faa45f9304244 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 19:59:56 +0900 Subject: [PATCH 02/11] =?UTF-8?q?Refactor:=20queryKey=20=EB=AF=B8=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EC=BD=94=EB=93=9C=EB=93=A4=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/queryKeys.ts | 98 ++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/constants/queryKeys.ts b/src/constants/queryKeys.ts index 0e5edf4..0e5fb60 100644 --- a/src/constants/queryKeys.ts +++ b/src/constants/queryKeys.ts @@ -1,47 +1,26 @@ import { getAuctionDetail } from '@/apis/auction/getAuctionDetail'; import { getAuctionLists } from '@/apis/auction/getAuctionList'; -import { getPurchasedArtworks } from '@/apis/mypage-buyer/artWork'; +import { getPurchasedArtworks } from '@/apis/mypageBuyer/artWork'; import { getAuthorArtworksExhibitions, getAuthorProfile, -} from '@/apis/mypage-author/author'; -import { AuthorProfileType } from '@/apis/mypage-author/type'; +} from '@/apis/mypageAuthor/author'; +import { AuthorProfileType } from '@/apis/mypageAuthor/type'; import { getAvailableArtworks } from '@/apis/auctionRegister/getAvailableArtworks'; import { getAuthorDetail } from '@/apis/author/getAuthorDetail'; import { getAuthorLists } from '@/apis/author/getAuthorLists'; -import { getArtistList } from '@/apis/Example/artist'; -import { TGetArtistListRequestParams } from '@/apis/Example/type'; import { getMainData } from '@/apis/main/main'; -import { getBuyerMypage } from '@/apis/mypage-buyer/myPage/myPage'; -import { getAuthorMypage } from '@/apis/mypage-author/myPage/myPage'; +import { getBuyerMypage } from '@/apis/mypageBuyer/myPage/myPage'; +import { getAuthorMypage } from '@/apis/mypageAuthor/myPage/myPage'; import { getExhibitions } from '@/apis/exhibition/getExhibitionList'; import { getExhibitionDetail } from '@/apis/exhibition/getExhibitionDetail'; -import { getExhibitAvailableArtwork } from '@/apis/exhibit-register/getExhibitAvailableArtwork'; -import { getExhibitBackgroundImages } from '@/apis/exhibit-register/getExhibitBackgroundImages'; - -/** - * 아티스트들의 정보를 받아오고, 관리하기 위한 쿼리 키로 함수와 묶어서 사용합니다. - * 단, pagenation 이 필요한 경우엔 다음과 같이 page 와 keyword 를 이용해 작성합니다. 이 때, queryKey 지정 여부에 따라서 데이터를 다시 못 받아오는 경우도 있으니, 검색이 필요한 경우엔 keyword 와 연관되게 키를 지정 후 데이터를 가져옵니다. - * @author 홍규진 - * */ - -export const getArtistListQuery = () => { - return { - queryKey: ['artistList'], - queryFn: ({ page, keyword }: TGetArtistListRequestParams) => - getArtistList({ page, keyword }), - }; -}; - -/** - * 마이페이지 조회를 위한 쿼리 키 반환 함수 - * @returns 쿼리 키 배열 ['Mypage']을 반환하여 캐시를 사용자별로 관리할 수 있도록 설정 - * @author 노찬영 - */ -export const getMypageQueryKey = (role: 'author' | 'buyer') => ['Mypage', role]; - +import { getExhibitAvailableArtwork } from '@/apis/exhibitRegister/getExhibitAvailableArtwork'; +import { getExhibitBackgroundImages } from '@/apis/exhibitRegister/getExhibitBackgroundImages'; +import { getArtworkDetail } from '@/apis/artworkDetail/artwork'; +import { getArtworks } from '@/apis/artwork/artwork'; +import { saveAuthorBankInfo } from '@/apis/mypageAuthor/account'; /** * 작품 구매자 마이페이지 조회 API를 위한 React Query 설정 함수 * @returns queryKey와 queryFn을 포함한 객체를 반환하여 React Query에서 사용 가능하도록 설정 @@ -49,7 +28,7 @@ export const getMypageQueryKey = (role: 'author' | 'buyer') => ['Mypage', role]; */ export const getBuyerMypageQuery = () => { return { - queryKey: getMypageQueryKey('buyer'), + queryKey: ['Mypage', 'buyer'], queryFn: () => getBuyerMypage(), // 작품 구매자 마이페이지 데이터를 조회하는 함수 }; }; @@ -61,7 +40,7 @@ export const getBuyerMypageQuery = () => { */ export const getAuthorMypageQuery = () => { return { - queryKey: getMypageQueryKey('author'), + queryKey: ['Mypage', 'author'], queryFn: () => getAuthorMypage(), // 작가 마이페이지 데이터를 조회하는 함수 }; }; @@ -101,14 +80,6 @@ export const getAuthorArtworksExhibitionsQuery = () => { }; }; -/** - * 작품 구매자 계정 정보 수정 쿼리 키 함수 - * @returns ['updateUserInfo'] 형태의 배열 반환 - * @example - queryClient.invalidateQueries(getUpdateUserInfoQueryKey()); - * @author 노찬영 - */ -export const getUpdateUserInfoQueryKey = () => ['updateUserInfo']; - /** * 작가 계정 정보 수정 쿼리 키 함수 * @returns ['updateAuthorInfo'] 형태의 배열 반환 @@ -133,6 +104,21 @@ export const getUpdateAuthorProfileQueryKey = () => ['updateAuthorProfile']; * @author 노찬영 */ +/** + * 작가 은행 정보 수정 쿼리 + * @param type + * @returns + * @author 홍규진 + */ +export const saveAuthorBankInfoMutation = () => { + return { + mutationKey: ['saveAuthorBankInfo'], + mutationFn: saveAuthorBankInfo, + }; +}; + + + export const getAuthorProfileQuery = (type: AuthorProfileType) => { return { queryKey: ['authorProfile'], @@ -272,3 +258,33 @@ export const getExhibitAvailableArtworkQuery = () => { queryFn: getExhibitAvailableArtwork, }; }; + +/** + * 작품 상세 조회 쿼리 + * @author 홍규진 + * */ +export const getArtworkDetailQuery = (artworkId: number) => { + return { + queryKey: ['artworkDetail', artworkId], + queryFn: () => getArtworkDetail(artworkId), + }; +}; + +/** + * 작품 리스트 조회 쿼리 + * @author 홍규진 + * */ +export const getArtworkListQuery = ( + page: number, + pageSize: number, + themes: string[], + sizes: string[], + forms: string[], + sortingKey?: 'latest' | 'popular' +) => { + return { + queryKey: ['artworkList', page, pageSize, themes, sizes, forms, sortingKey], + queryFn: () => + getArtworks(page, pageSize, themes, sizes, forms, sortingKey), + }; +}; From 6536cede43af4fb0a104db78a9cb82096dd21229 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:00:25 +0900 Subject: [PATCH 03/11] =?UTF-8?q?Delete:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=93=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/apis/useGetArtistList.ts | 30 ------------------------------ src/types/artist.ts | 4 ---- 2 files changed, 34 deletions(-) delete mode 100644 src/hooks/apis/useGetArtistList.ts delete mode 100644 src/types/artist.ts diff --git a/src/hooks/apis/useGetArtistList.ts b/src/hooks/apis/useGetArtistList.ts deleted file mode 100644 index 706ae1d..0000000 --- a/src/hooks/apis/useGetArtistList.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - TGetArtistListRequestParams, - TGetArtistListResponse, -} from '@apis/Example/type.ts'; -import { getArtistListQuery } from '@constants/queryKeys.ts'; -import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { getArtistList } from '@apis/Example/artist.ts'; - -export const useGetArtistList = ({ - page, - keyword, -}: TGetArtistListRequestParams) => { - const { - data: artistListData, - isLoading, - error, - } = useSuspenseQuery({ - queryKey: getArtistListQuery().queryKey, - queryFn: () => getArtistList({ page, keyword }), - staleTime: 1000 * 60 * 60, // 1시간 - gcTime: 1000 * 60 * 40, // 40분 - }); - - if (error) { - toast.error(error.message); - } - - return { artistListData, isLoading, error }; -}; diff --git a/src/types/artist.ts b/src/types/artist.ts deleted file mode 100644 index af215bb..0000000 --- a/src/types/artist.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type TArtist = { - artistId: number; - name: string; -}; From ff95304ee2daf63842624388887385d229c46be7 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:01:06 +0900 Subject: [PATCH 04/11] =?UTF-8?q?Delete:=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EB=AF=B8=EC=A4=80=EC=88=98=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/Example/artist.ts | 13 --- src/apis/Example/type.ts | 8 -- src/apis/artwork-detail/artwork.ts | 19 ----- src/apis/artwork-detail/postMySpace.ts | 37 -------- src/apis/artwork-detail/type.ts | 52 ------------ src/apis/artwork-like/like.ts | 18 ---- src/apis/artwork-like/type.ts | 14 ---- .../getExhibitAvailableArtwork.ts | 15 ---- .../getExhibitBackgroundImages.ts | 16 ---- src/apis/exhibit-register/types.ts | 10 --- src/apis/mypage-author/account.ts | 19 ----- src/apis/mypage-author/author.ts | 61 -------------- src/apis/mypage-author/myPage/myPage.ts | 22 ----- src/apis/mypage-author/myPage/type.ts | 55 ------------ src/apis/mypage-author/profile.ts | 22 ----- src/apis/mypage-author/type.ts | 84 ------------------- src/apis/mypage-buyer/artBuyer.ts | 25 ------ src/apis/mypage-buyer/artWork.ts | 15 ---- src/apis/mypage-buyer/myPage/myPage.ts | 24 ------ src/apis/mypage-buyer/myPage/type.ts | 76 ----------------- src/apis/mypage-buyer/type.ts | 18 ---- 21 files changed, 623 deletions(-) delete mode 100644 src/apis/Example/artist.ts delete mode 100644 src/apis/Example/type.ts delete mode 100644 src/apis/artwork-detail/artwork.ts delete mode 100644 src/apis/artwork-detail/postMySpace.ts delete mode 100644 src/apis/artwork-detail/type.ts delete mode 100644 src/apis/artwork-like/like.ts delete mode 100644 src/apis/artwork-like/type.ts delete mode 100644 src/apis/exhibit-register/getExhibitAvailableArtwork.ts delete mode 100644 src/apis/exhibit-register/getExhibitBackgroundImages.ts delete mode 100644 src/apis/exhibit-register/types.ts delete mode 100644 src/apis/mypage-author/account.ts delete mode 100644 src/apis/mypage-author/author.ts delete mode 100644 src/apis/mypage-author/myPage/myPage.ts delete mode 100644 src/apis/mypage-author/myPage/type.ts delete mode 100644 src/apis/mypage-author/profile.ts delete mode 100644 src/apis/mypage-author/type.ts delete mode 100644 src/apis/mypage-buyer/artBuyer.ts delete mode 100644 src/apis/mypage-buyer/artWork.ts delete mode 100644 src/apis/mypage-buyer/myPage/myPage.ts delete mode 100644 src/apis/mypage-buyer/myPage/type.ts delete mode 100644 src/apis/mypage-buyer/type.ts diff --git a/src/apis/Example/artist.ts b/src/apis/Example/artist.ts deleted file mode 100644 index 94fd3fb..0000000 --- a/src/apis/Example/artist.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TGetArtistListRequestParams, TGetArtistListResponse } from './type'; - -export const getArtistList = async ({ - page, - keyword, -}: TGetArtistListRequestParams): Promise => { - const response = await instance.get>( - `/api/v1/artist/list?page=${page}&keyword=${keyword}` - ); - return response.data.result; -}; diff --git a/src/apis/Example/type.ts b/src/apis/Example/type.ts deleted file mode 100644 index 194deaf..0000000 --- a/src/apis/Example/type.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TArtist } from '@/types/artist'; - -export type TGetArtistListResponse = TArtist[]; - -export type TGetArtistListRequestParams = { - page?: number; - keyword?: string; -}; diff --git a/src/apis/artwork-detail/artwork.ts b/src/apis/artwork-detail/artwork.ts deleted file mode 100644 index a5e85bf..0000000 --- a/src/apis/artwork-detail/artwork.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TArtworkDetailResult } from './type'; -import { TGetResponse } from '@apis/type'; - -/** - * 특정 작품의 상세 정보를 가져오는 API 함수 - * @param artworkId 작품 ID - * @returns 작품 상세 정보 - * @author 김서윤 - */ - -export const getArtworkDetail = async ( - artworkId: number -): Promise => { - const response = await instance.get>( - `/api/artworks/${artworkId}` - ); - return response.data.result; -}; diff --git a/src/apis/artwork-detail/postMySpace.ts b/src/apis/artwork-detail/postMySpace.ts deleted file mode 100644 index 0842d90..0000000 --- a/src/apis/artwork-detail/postMySpace.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TMySpaceFormData } from './type'; - -/** - * 내 공간 등록 API 함수 - * @param data 공간 등록 폼 데이터 (파일 전송을 위해 FormData 형태로 변환됩니다.) - * @returns 등록 결과를 포함한 응답(TGetResponse) - * @author 김서윤 - */ - -export const postMySpace = async ( - data: TMySpaceFormData -): Promise> => { - // 이미지 파일과 텍스트 필드를 함께 전송하기 위해 FormData 사용 - const formData = new FormData(); - - if (data.images) { - formData.append('images', data.images); - } - - // 나머지 텍스트 데이터 추가 - formData.append('name', data.name); - formData.append('area', data.area); - - const response = await instance.post>( - '/api/userspace', - formData, - { - headers: { - 'Content-Type': 'multipart/form-data', - }, - } - ); - console.log(response.data); - return response.data; -}; diff --git a/src/apis/artwork-detail/type.ts b/src/apis/artwork-detail/type.ts deleted file mode 100644 index da1000b..0000000 --- a/src/apis/artwork-detail/type.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 작풍 상세 조회 API 타입 정의 - * @author 김서윤 - */ - -export interface TArtworkDetailFixedInfo { - author_name: string; - artwork_title: string; - artwork_image: string[]; - year: string; - dimensions: string; - material: string; - size: number; - category: string; - genre: string; -} - -export interface TArtworkDetailTabData { - description: string; - userspace?: TUserSpace[]; - author_id: number; - author_name: string; - author_image: string; - work_style: string | null; - artwork_count: number; - exhibition_count: number; - other_artworks: TOtherArtwork[]; -} - -export interface TArtworkDetailResult { - fixed_info: TArtworkDetailFixedInfo; - tab_data: TArtworkDetailTabData; -} - -export type TUserSpace = { - userspace_id: number; - name: string; - image_url: string; - area: string; -}; - -export type TOtherArtwork = { - id: number; - title: string; - thumbnail: string; -}; - -export type TMySpaceFormData = { - images?: File; - name: string; - area: string; -}; diff --git a/src/apis/artwork-like/like.ts b/src/apis/artwork-like/like.ts deleted file mode 100644 index c19639c..0000000 --- a/src/apis/artwork-like/like.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TToggleLikeResult } from './type'; -import { TGetResponse } from '@/apis/type'; - -/** - * 작품 좋아요 토글 API 호출 - * @param artworkId 작품 ID - * @returns 좋아요 추가 또는 취소 결과 - * @author 김서윤 - */ -export const toggleArtworkLike = async ( - artworkId: number -): Promise => { - const response = await instance.post>( - `/api/artworks/${artworkId}/like` - ); - return response.data.result; -}; diff --git a/src/apis/artwork-like/type.ts b/src/apis/artwork-like/type.ts deleted file mode 100644 index 30430cc..0000000 --- a/src/apis/artwork-like/type.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 작풍 좋아요 API 타입 정의 - * @author 김서윤 - */ - -export interface TToggleLikeResult { - message: string; - newFavorite?: { - created_at: string; - id: number; - user_id: number; - artwork_id: string; - }; -} diff --git a/src/apis/exhibit-register/getExhibitAvailableArtwork.ts b/src/apis/exhibit-register/getExhibitAvailableArtwork.ts deleted file mode 100644 index 44fe21c..0000000 --- a/src/apis/exhibit-register/getExhibitAvailableArtwork.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TGetExhibitAvailableArtworksResponse } from './types'; -/** - * 전시 가능 작품 조회 API - * @author 홍규진 - * */ -export const getExhibitAvailableArtwork = async (): Promise< - TGetExhibitAvailableArtworksResponse[] -> => { - const response = await instance.get< - TGetResponse - >('/api/author/artworks'); - return response.data.result; -}; diff --git a/src/apis/exhibit-register/getExhibitBackgroundImages.ts b/src/apis/exhibit-register/getExhibitBackgroundImages.ts deleted file mode 100644 index f6c61ed..0000000 --- a/src/apis/exhibit-register/getExhibitBackgroundImages.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TGetBackgroundImagesResponse } from './types'; -/** - * 배경 이미지 조회 API - * @author 홍규진 - * */ -export const getExhibitBackgroundImages = async (): Promise< - TGetBackgroundImagesResponse[] -> => { - const response = await instance.get< - TGetResponse - >('/api/exhibitions/backgrounds'); - console.log(response.data); - return response.data.result; -}; diff --git a/src/apis/exhibit-register/types.ts b/src/apis/exhibit-register/types.ts deleted file mode 100644 index f776f57..0000000 --- a/src/apis/exhibit-register/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type TGetBackgroundImagesResponse = { - id: number; - name: string; - background_url: string; -}; -export type TGetExhibitAvailableArtworksResponse = { - id: number; - title: string; - thumbnail_image_url: string; -}; diff --git a/src/apis/mypage-author/account.ts b/src/apis/mypage-author/account.ts deleted file mode 100644 index 3e3901d..0000000 --- a/src/apis/mypage-author/account.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TSaveBankInfo } from './type'; - -/** - * 작가 계좌 정보 저장 API 호출 함수 - * @param bankInfo - 저장할 계좌 정보 - * @returns 저장된 계좌 정보(TSaveBankInfo) - * @author 노찬영 - */ -export const saveAuthorBankInfo = async ( - bankInfo: TSaveBankInfo -): Promise => { - const response = await instance.post>( - '/api/author/bank', - bankInfo - ); - return response.data.result; -}; diff --git a/src/apis/mypage-author/author.ts b/src/apis/mypage-author/author.ts deleted file mode 100644 index ee35de5..0000000 --- a/src/apis/mypage-author/author.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { instance } from '@/apis/axios'; -import { - AuthorProfileType, - TAuthorArtworksExhibitions, - TAuthorProfile, - TUpdateAuthorInfo, -} from './type'; -import { TGetResponse } from '../type'; - -/** - * 작가 프로필 조회 API 호출 함수 - * @param type - 조회할 프로필 타입 ('default', 'intro', 'info') - * @returns API 응답(TAuthorProfileResponse) - * @throws {Error} API 요청 실패 시 오류 발생 - * @author 노찬영 - **/ -export const getAuthorProfile = async ( - type: AuthorProfileType -): Promise => { - try { - const response = await instance.get>( - `/api/author?type=${type}` - ); - return response.data.result; - } catch (error) { - console.error('작가 프로필 조회 실패:', error); - throw error; - } -}; - -/** - * 작가의 작품, 경매 중인 작품, 진행 중인 전시 정보를 조회하는 API - * @returns {TAuthorArtworksExhibitions} API 응답 데이터 - * @throws {Error} API 요청 실패 시 오류 발생 - * @author 노찬영 - **/ -export const getAuthorArtworksExhibitions = - async (): Promise => { - try { - const response = await instance.get< - TGetResponse - >('/api/author/artworks-exhibitions'); - return response.data.result; - } catch (error) { - console.error('작가 작가 작품/전시 조회 실패:', error); - throw error; - } - }; - -/** - * 작가 정보 수정 API 호출 함수 - * @param authorId - 작가 ID - * @param authorInfo - 수정할 작가 정보 (nickname, address, birth, author_image_url, introduction_image_url) - * @author 노찬양 - **/ -export const updateAuthorInfo = async ( - authorId: number, - authorInfo: TUpdateAuthorInfo -): Promise => { - await instance.patch(`/api/author/update`, authorInfo); -}; diff --git a/src/apis/mypage-author/myPage/myPage.ts b/src/apis/mypage-author/myPage/myPage.ts deleted file mode 100644 index 9d69f16..0000000 --- a/src/apis/mypage-author/myPage/myPage.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TArtistMypage } from './type'; -import { TGetResponse } from '@/apis/type'; - -/** - * 작가의 마이페이지 정보를 조회하는 API 호출 함수 - * @returns 사용자 마이페이지 정보(TArtistMypage) - * @author 노찬영 - **/ -export const getAuthorMypage = async (): Promise => { - const AUTHOR_TOKEN = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InJ5dXNlb25naG83QGRndS5hYy5rciIsImlhdCI6MTczOTIwNDY5NiwiZXhwIjoxNzQxNzk2Njk2fQ.iWwnAhIzse5UwvHpR5uWa2o0HRM5G14ikeAtYf_BDec'; - console.log('작가 accessToken:', AUTHOR_TOKEN); - - localStorage.setItem('accessToken', AUTHOR_TOKEN); - instance.defaults.headers.common.Authorization = `Bearer ${AUTHOR_TOKEN}`; - - const response = await instance.get>( - `/api/mypage` - ); - return response.data.result; -}; diff --git a/src/apis/mypage-author/myPage/type.ts b/src/apis/mypage-author/myPage/type.ts deleted file mode 100644 index de2330f..0000000 --- a/src/apis/mypage-author/myPage/type.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 작가 마이페이지 조회 API 타입 정의 - * @author 노찬영 - */ - -// 작가 마이페이지 타입 -export type TArtistMypage = { - author: TAuthor; - auctions: TAuction[]; - storage: { - artworks: TArtwork[]; - exhibitions: TExhibition[]; - }; -}; - -// 작가 정보 타입 -export type TAuthor = { - name: string; - profile_image_url: string; -}; - -// 경매 타입 -export type TAuction = { - auction_id: number; - bid_date: string; - bid_price: number; - status: string; - auction: { - artwork_id: number; - artwork: { - title: string; - author: { - author_name: string; - author_image_url: string; - }; - }; - }; -}; - -// 작품 타입 -export type TArtwork = { - id: number; - title: string; - author?: { name: string }; - thumbnail_image_url: string; - height: string; - width: string; -}; - -// 전시 타입 -export type TExhibition = { - id: number; - title: string; - image_url: string; -}; diff --git a/src/apis/mypage-author/profile.ts b/src/apis/mypage-author/profile.ts deleted file mode 100644 index 4db29fc..0000000 --- a/src/apis/mypage-author/profile.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TUpdateAuthorProfile } from './type'; - -/** - * 작가 프로필 정보 개별 수정 API 호출 함수 - * @param data - 수정할 속성(attribute)과 값(value) - * @returns void (응답 데이터를 처리하지 않음) - * @author 노찬영 - */ -export const updateAuthorProfile = async ( - data: TUpdateAuthorProfile -): Promise => { - const response = await instance.patch>( - '/api/author/profile', - data - ); - - if (!response.data.isSuccess) { - throw new Error('작가 프로필 수정에 실패했습니다.'); - } -}; diff --git a/src/apis/mypage-author/type.ts b/src/apis/mypage-author/type.ts deleted file mode 100644 index e6894c8..0000000 --- a/src/apis/mypage-author/type.ts +++ /dev/null @@ -1,84 +0,0 @@ -export type AuthorProfileType = 'default' | 'intro' | 'info'; - -export interface EducationInfo { - school: string; - major: string; - status: string; - start_date: string; - end_date: string; -} - -export interface AwardInfo { - date: string; - description: string; -} - -export interface ExperienceInfo { - date: string; - description: string; -} - -export type TAuthorProfile = { - isSuccess: boolean; - code: string; - message: string; - author_name: string; - author_image_url: string; - email: string; - description: string; - work_style: string; - education: EducationInfo[]; - award: AwardInfo[]; - experience: ExperienceInfo[]; -}; - -export type TAuthorArtworksExhibitions = { - author: { - id: number; - }; - artworks: { - id: number; - title: string; - thumbnail_image_url: string; - }[]; - auction_artworks: { - auction_id: number; - auction_period: string; - artwork: { - id: number; - title: string; - thumbnail_image_url: string; - }; - }[]; - exhibitions: { - id: number; - title: string; - image_url: string; - }[]; -}; - -export type TSaveBankInfo = { - bank_name: string; - account_holder: string; - account_number: string; -}; - -export type TUpdateAuthorInfo = { - nickname?: string; - address?: string; - birth?: string; - author_image_url?: string; - introduction_image_url?: string; -}; - -export type AuthorProfileAttribute = - | 'description' - | 'work_style' - | 'education' - | 'award' - | 'experience'; - -export type TUpdateAuthorProfile = { - attribute: AuthorProfileAttribute; - value: string; -}; diff --git a/src/apis/mypage-buyer/artBuyer.ts b/src/apis/mypage-buyer/artBuyer.ts deleted file mode 100644 index 29cc50b..0000000 --- a/src/apis/mypage-buyer/artBuyer.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { TUpdateUserInfo } from './type'; - -/** - * 사용자 정보 수정 API 호출 함수 - * @param userInfo - 수정할 사용자 정보 (nickname, address, birth) - * @returns API 응답(TGetResponse) - * @author 노찬영 - */ - -export const updateUserInfo = async ( - userInfo: TUpdateUserInfo -): Promise> => { - try { - const response = await instance.patch>( - `/api/user/update`, - userInfo - ); - return response.data; - } catch (error) { - console.error('사용자 정보 수정 실패:', error); - throw error; - } -}; diff --git a/src/apis/mypage-buyer/artWork.ts b/src/apis/mypage-buyer/artWork.ts deleted file mode 100644 index 1c04cf8..0000000 --- a/src/apis/mypage-buyer/artWork.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TPurchasedArtwork } from './type'; -import { TGetResponse } from '../type'; - -/** - * 구매한 작품 리스트를 조회하는 API 호출 함수 - * @returns 구매한 작품 리스트(TPurchasedArtwork) - * @author 노찬영 - **/ -export const getPurchasedArtworks = async (): Promise => { - const response = await instance.get>( - '/api/user/purchased-artworks' - ); - return response.data.result; -}; diff --git a/src/apis/mypage-buyer/myPage/myPage.ts b/src/apis/mypage-buyer/myPage/myPage.ts deleted file mode 100644 index 310751c..0000000 --- a/src/apis/mypage-buyer/myPage/myPage.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { instance } from '@/apis/axios'; -import { TBuyerMypage } from './type'; -import { TGetResponse } from '@/apis/type'; - -/** - * 작품 구매자의 마이페이지 정보를 조회하는 API 호출 함수 - * @returns 사용자 마이페이지 정보(TBuyerMypage) - * @author 노찬영 - **/ -export const getBuyerMypage = async (): Promise => { - const BUYER_TOKEN = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh5c29uZzR1QGdtYWlsLmNvbSIsImlhdCI6MTczOTU1MzczMSwiZXhwIjoxNzQyMTQ1NzMxfQ.6ePJhRS1JUNK9BPIOk9oXrYoggois21uBtGsp4gvKrU'; - console.log('구매자 accessToken:', BUYER_TOKEN); - - // 구매자 토큰으로 변경 - localStorage.setItem('accessToken', BUYER_TOKEN); - instance.defaults.headers.common.Authorization = `Bearer ${BUYER_TOKEN}`; - - const response = await instance.get>( - `/api/mypage` - ); - - return response.data.result; -}; diff --git a/src/apis/mypage-buyer/myPage/type.ts b/src/apis/mypage-buyer/myPage/type.ts deleted file mode 100644 index b1994d5..0000000 --- a/src/apis/mypage-buyer/myPage/type.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * 작품 구매자 마이페이지 조회 API 타입 정의 - * @author 노찬영 - */ - -// 작품 구매자 타입 - -export type TBuyerMypage = { - buyer: { - name: string; - profile_image_url: string; - }; - paymentCounts: TPaymentCount[]; - auctions: TAuction[]; - payments: TPayment[]; - myCollection: { - artworks: TArtwork[]; - exhibitions: TExhibition[]; - }; -}; - -export type TPaymentCount = { - pending: number; - completed: number; - received: number; -}; - -export type TAuction = { - auction_id: number; - bid_date: string; - bid_price: number; - status: string; - auction: { - artwork_id: number; - artwork: { - title: string; - author: { - author_name: string; - author_image_url: string; - }; - }; - }; -}; - -export type TPayment = { - id: number; - auction_id: number; - payment_price: number; - created_at: string; - payment_status: string; - auction: { - artwork_id: number; - artwork: { - title: string; - author: { - author_name: string; - author_image_url: string; - }; - }; - }; -}; - -export type TArtwork = { - id: number; - title: string; - thumbnail_image_url: string; - width?: string; - height?: string; - author?: { author_name: string; author_image_url: string }; -}; - -export type TExhibition = { - id: number; - title: string; - image_url: string; -}; diff --git a/src/apis/mypage-buyer/type.ts b/src/apis/mypage-buyer/type.ts deleted file mode 100644 index 59f4166..0000000 --- a/src/apis/mypage-buyer/type.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 구매한 작품 데이터 타입 - * @author 노찬영 - **/ - -export interface TPurchasedArtwork { - artworkId: number; // 작품 ID - authorName: string; // 작가 이름 - title: string; // 작품 제목 - size: string; // 작품 크기 - isLiked: boolean; // 좋아요 여부 -} - -export interface TUpdateUserInfo { - nickname?: string; - address?: string; - birth?: string; -} From a2c69d61d3fa7fbaf6973d212be91c12b495fef2 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:01:26 +0900 Subject: [PATCH 05/11] =?UTF-8?q?Feat:=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A4=80=EC=88=98=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/artworkDetail/artwork.ts | 19 +++++ src/apis/artworkDetail/postMySpace.ts | 37 ++++++++ src/apis/artworkDetail/type.ts | 52 ++++++++++++ src/apis/artworkLike/like.ts | 18 ++++ src/apis/artworkLike/type.ts | 14 ++++ .../auctionRegister/postAuctionRegister.ts | 6 +- src/apis/auctionRegister/type.ts | 4 +- .../getExhibitAvailableArtwork.ts | 15 ++++ .../getExhibitBackgroundImages.ts | 16 ++++ .../exhibitRegister/postExhibitRegister.ts | 22 +++++ src/apis/exhibitRegister/types.ts | 15 ++++ src/apis/mypageAuthor/account.ts | 19 +++++ src/apis/mypageAuthor/author.ts | 61 ++++++++++++++ src/apis/mypageAuthor/myPage/myPage.ts | 22 +++++ src/apis/mypageAuthor/myPage/type.ts | 55 ++++++++++++ src/apis/mypageAuthor/profile.ts | 22 +++++ src/apis/mypageAuthor/type.ts | 84 +++++++++++++++++++ src/apis/mypageBuyer/artBuyer.ts | 15 ++++ src/apis/mypageBuyer/artWork.ts | 15 ++++ src/apis/mypageBuyer/myPage/myPage.ts | 24 ++++++ src/apis/mypageBuyer/myPage/type.ts | 76 +++++++++++++++++ src/apis/mypageBuyer/type.ts | 18 ++++ src/apis/register/oAuthLogin.ts | 7 ++ src/apis/register/postAuthRegister.ts | 6 ++ src/apis/register/type.ts | 12 +++ 25 files changed, 651 insertions(+), 3 deletions(-) create mode 100644 src/apis/artworkDetail/artwork.ts create mode 100644 src/apis/artworkDetail/postMySpace.ts create mode 100644 src/apis/artworkDetail/type.ts create mode 100644 src/apis/artworkLike/like.ts create mode 100644 src/apis/artworkLike/type.ts create mode 100644 src/apis/exhibitRegister/getExhibitAvailableArtwork.ts create mode 100644 src/apis/exhibitRegister/getExhibitBackgroundImages.ts create mode 100644 src/apis/exhibitRegister/postExhibitRegister.ts create mode 100644 src/apis/exhibitRegister/types.ts create mode 100644 src/apis/mypageAuthor/account.ts create mode 100644 src/apis/mypageAuthor/author.ts create mode 100644 src/apis/mypageAuthor/myPage/myPage.ts create mode 100644 src/apis/mypageAuthor/myPage/type.ts create mode 100644 src/apis/mypageAuthor/profile.ts create mode 100644 src/apis/mypageAuthor/type.ts create mode 100644 src/apis/mypageBuyer/artBuyer.ts create mode 100644 src/apis/mypageBuyer/artWork.ts create mode 100644 src/apis/mypageBuyer/myPage/myPage.ts create mode 100644 src/apis/mypageBuyer/myPage/type.ts create mode 100644 src/apis/mypageBuyer/type.ts diff --git a/src/apis/artworkDetail/artwork.ts b/src/apis/artworkDetail/artwork.ts new file mode 100644 index 0000000..a5e85bf --- /dev/null +++ b/src/apis/artworkDetail/artwork.ts @@ -0,0 +1,19 @@ +import { instance } from '@/apis/axios'; +import { TArtworkDetailResult } from './type'; +import { TGetResponse } from '@apis/type'; + +/** + * 특정 작품의 상세 정보를 가져오는 API 함수 + * @param artworkId 작품 ID + * @returns 작품 상세 정보 + * @author 김서윤 + */ + +export const getArtworkDetail = async ( + artworkId: number +): Promise => { + const response = await instance.get>( + `/api/artworks/${artworkId}` + ); + return response.data.result; +}; diff --git a/src/apis/artworkDetail/postMySpace.ts b/src/apis/artworkDetail/postMySpace.ts new file mode 100644 index 0000000..0842d90 --- /dev/null +++ b/src/apis/artworkDetail/postMySpace.ts @@ -0,0 +1,37 @@ +import { instance } from '@/apis/axios'; +import { TGetResponse } from '@/apis/type'; +import { TMySpaceFormData } from './type'; + +/** + * 내 공간 등록 API 함수 + * @param data 공간 등록 폼 데이터 (파일 전송을 위해 FormData 형태로 변환됩니다.) + * @returns 등록 결과를 포함한 응답(TGetResponse) + * @author 김서윤 + */ + +export const postMySpace = async ( + data: TMySpaceFormData +): Promise> => { + // 이미지 파일과 텍스트 필드를 함께 전송하기 위해 FormData 사용 + const formData = new FormData(); + + if (data.images) { + formData.append('images', data.images); + } + + // 나머지 텍스트 데이터 추가 + formData.append('name', data.name); + formData.append('area', data.area); + + const response = await instance.post>( + '/api/userspace', + formData, + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + } + ); + console.log(response.data); + return response.data; +}; diff --git a/src/apis/artworkDetail/type.ts b/src/apis/artworkDetail/type.ts new file mode 100644 index 0000000..da1000b --- /dev/null +++ b/src/apis/artworkDetail/type.ts @@ -0,0 +1,52 @@ +/** + * 작풍 상세 조회 API 타입 정의 + * @author 김서윤 + */ + +export interface TArtworkDetailFixedInfo { + author_name: string; + artwork_title: string; + artwork_image: string[]; + year: string; + dimensions: string; + material: string; + size: number; + category: string; + genre: string; +} + +export interface TArtworkDetailTabData { + description: string; + userspace?: TUserSpace[]; + author_id: number; + author_name: string; + author_image: string; + work_style: string | null; + artwork_count: number; + exhibition_count: number; + other_artworks: TOtherArtwork[]; +} + +export interface TArtworkDetailResult { + fixed_info: TArtworkDetailFixedInfo; + tab_data: TArtworkDetailTabData; +} + +export type TUserSpace = { + userspace_id: number; + name: string; + image_url: string; + area: string; +}; + +export type TOtherArtwork = { + id: number; + title: string; + thumbnail: string; +}; + +export type TMySpaceFormData = { + images?: File; + name: string; + area: string; +}; diff --git a/src/apis/artworkLike/like.ts b/src/apis/artworkLike/like.ts new file mode 100644 index 0000000..c19639c --- /dev/null +++ b/src/apis/artworkLike/like.ts @@ -0,0 +1,18 @@ +import { instance } from '@/apis/axios'; +import { TToggleLikeResult } from './type'; +import { TGetResponse } from '@/apis/type'; + +/** + * 작품 좋아요 토글 API 호출 + * @param artworkId 작품 ID + * @returns 좋아요 추가 또는 취소 결과 + * @author 김서윤 + */ +export const toggleArtworkLike = async ( + artworkId: number +): Promise => { + const response = await instance.post>( + `/api/artworks/${artworkId}/like` + ); + return response.data.result; +}; diff --git a/src/apis/artworkLike/type.ts b/src/apis/artworkLike/type.ts new file mode 100644 index 0000000..30430cc --- /dev/null +++ b/src/apis/artworkLike/type.ts @@ -0,0 +1,14 @@ +/** + * 작풍 좋아요 API 타입 정의 + * @author 김서윤 + */ + +export interface TToggleLikeResult { + message: string; + newFavorite?: { + created_at: string; + id: number; + user_id: number; + artwork_id: string; + }; +} diff --git a/src/apis/auctionRegister/postAuctionRegister.ts b/src/apis/auctionRegister/postAuctionRegister.ts index 9559b5d..9f71537 100644 --- a/src/apis/auctionRegister/postAuctionRegister.ts +++ b/src/apis/auctionRegister/postAuctionRegister.ts @@ -11,6 +11,10 @@ import { TAuctionRegisterFormData } from './type'; export const postAuctionRegister = async ( data: TAuctionRegisterFormData ): Promise> => { - const response = await instance.post('/api/auction/register', data); + const formData = new FormData(); + formData.append('artwork_id', data.artwork_id.toString()); + formData.append('start_price', data.start_price.toString()); + + const response = await instance.post('/api/auction/register', formData); return response.data; }; diff --git a/src/apis/auctionRegister/type.ts b/src/apis/auctionRegister/type.ts index e6779ec..29fc85d 100644 --- a/src/apis/auctionRegister/type.ts +++ b/src/apis/auctionRegister/type.ts @@ -13,6 +13,6 @@ export type TGetAvailableArtworksResponse = { * @author 홍규진 */ export type TAuctionRegisterFormData = { - artwork_id: number | null; - start_price: number | null; + artwork_id: number; + start_price: number; }; diff --git a/src/apis/exhibitRegister/getExhibitAvailableArtwork.ts b/src/apis/exhibitRegister/getExhibitAvailableArtwork.ts new file mode 100644 index 0000000..44fe21c --- /dev/null +++ b/src/apis/exhibitRegister/getExhibitAvailableArtwork.ts @@ -0,0 +1,15 @@ +import { instance } from '@/apis/axios'; +import { TGetResponse } from '@/apis/type'; +import { TGetExhibitAvailableArtworksResponse } from './types'; +/** + * 전시 가능 작품 조회 API + * @author 홍규진 + * */ +export const getExhibitAvailableArtwork = async (): Promise< + TGetExhibitAvailableArtworksResponse[] +> => { + const response = await instance.get< + TGetResponse + >('/api/author/artworks'); + return response.data.result; +}; diff --git a/src/apis/exhibitRegister/getExhibitBackgroundImages.ts b/src/apis/exhibitRegister/getExhibitBackgroundImages.ts new file mode 100644 index 0000000..f6c61ed --- /dev/null +++ b/src/apis/exhibitRegister/getExhibitBackgroundImages.ts @@ -0,0 +1,16 @@ +import { instance } from '@/apis/axios'; +import { TGetResponse } from '@/apis/type'; +import { TGetBackgroundImagesResponse } from './types'; +/** + * 배경 이미지 조회 API + * @author 홍규진 + * */ +export const getExhibitBackgroundImages = async (): Promise< + TGetBackgroundImagesResponse[] +> => { + const response = await instance.get< + TGetResponse + >('/api/exhibitions/backgrounds'); + console.log(response.data); + return response.data.result; +}; diff --git a/src/apis/exhibitRegister/postExhibitRegister.ts b/src/apis/exhibitRegister/postExhibitRegister.ts new file mode 100644 index 0000000..c86a19b --- /dev/null +++ b/src/apis/exhibitRegister/postExhibitRegister.ts @@ -0,0 +1,22 @@ +/** + * 전시 등록 API 호출 + * 멀티파트로 제공해야하는 데이터는 다음과 같이 제공해야합니다. + * @author 홍규진 + */ + +import { instance } from '../axios'; +import { TPostExhibitionRegisterFormData } from './types'; + +export const postExhibitionRegister = async ( + data: TPostExhibitionRegisterFormData +) => { + const formData = new FormData(); + formData.append('title', data.title); + formData.append('exhibit_image', data.exhibit_image); + + await instance.post('/api/exhibition', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); +}; diff --git a/src/apis/exhibitRegister/types.ts b/src/apis/exhibitRegister/types.ts new file mode 100644 index 0000000..20f357b --- /dev/null +++ b/src/apis/exhibitRegister/types.ts @@ -0,0 +1,15 @@ +export type TGetBackgroundImagesResponse = { + id: number; + name: string; + background_url: string; +}; +export type TGetExhibitAvailableArtworksResponse = { + id: number; + title: string; + thumbnail_image_url: string; +}; + +export type TPostExhibitionRegisterFormData = { + title: string; + exhibit_image: File; +}; diff --git a/src/apis/mypageAuthor/account.ts b/src/apis/mypageAuthor/account.ts new file mode 100644 index 0000000..3e3901d --- /dev/null +++ b/src/apis/mypageAuthor/account.ts @@ -0,0 +1,19 @@ +import { instance } from '@/apis/axios'; +import { TGetResponse } from '@/apis/type'; +import { TSaveBankInfo } from './type'; + +/** + * 작가 계좌 정보 저장 API 호출 함수 + * @param bankInfo - 저장할 계좌 정보 + * @returns 저장된 계좌 정보(TSaveBankInfo) + * @author 노찬영 + */ +export const saveAuthorBankInfo = async ( + bankInfo: TSaveBankInfo +): Promise => { + const response = await instance.post>( + '/api/author/bank', + bankInfo + ); + return response.data.result; +}; diff --git a/src/apis/mypageAuthor/author.ts b/src/apis/mypageAuthor/author.ts new file mode 100644 index 0000000..ee35de5 --- /dev/null +++ b/src/apis/mypageAuthor/author.ts @@ -0,0 +1,61 @@ +import { instance } from '@/apis/axios'; +import { + AuthorProfileType, + TAuthorArtworksExhibitions, + TAuthorProfile, + TUpdateAuthorInfo, +} from './type'; +import { TGetResponse } from '../type'; + +/** + * 작가 프로필 조회 API 호출 함수 + * @param type - 조회할 프로필 타입 ('default', 'intro', 'info') + * @returns API 응답(TAuthorProfileResponse) + * @throws {Error} API 요청 실패 시 오류 발생 + * @author 노찬영 + **/ +export const getAuthorProfile = async ( + type: AuthorProfileType +): Promise => { + try { + const response = await instance.get>( + `/api/author?type=${type}` + ); + return response.data.result; + } catch (error) { + console.error('작가 프로필 조회 실패:', error); + throw error; + } +}; + +/** + * 작가의 작품, 경매 중인 작품, 진행 중인 전시 정보를 조회하는 API + * @returns {TAuthorArtworksExhibitions} API 응답 데이터 + * @throws {Error} API 요청 실패 시 오류 발생 + * @author 노찬영 + **/ +export const getAuthorArtworksExhibitions = + async (): Promise => { + try { + const response = await instance.get< + TGetResponse + >('/api/author/artworks-exhibitions'); + return response.data.result; + } catch (error) { + console.error('작가 작가 작품/전시 조회 실패:', error); + throw error; + } + }; + +/** + * 작가 정보 수정 API 호출 함수 + * @param authorId - 작가 ID + * @param authorInfo - 수정할 작가 정보 (nickname, address, birth, author_image_url, introduction_image_url) + * @author 노찬양 + **/ +export const updateAuthorInfo = async ( + authorId: number, + authorInfo: TUpdateAuthorInfo +): Promise => { + await instance.patch(`/api/author/update`, authorInfo); +}; diff --git a/src/apis/mypageAuthor/myPage/myPage.ts b/src/apis/mypageAuthor/myPage/myPage.ts new file mode 100644 index 0000000..9d69f16 --- /dev/null +++ b/src/apis/mypageAuthor/myPage/myPage.ts @@ -0,0 +1,22 @@ +import { instance } from '@/apis/axios'; +import { TArtistMypage } from './type'; +import { TGetResponse } from '@/apis/type'; + +/** + * 작가의 마이페이지 정보를 조회하는 API 호출 함수 + * @returns 사용자 마이페이지 정보(TArtistMypage) + * @author 노찬영 + **/ +export const getAuthorMypage = async (): Promise => { + const AUTHOR_TOKEN = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InJ5dXNlb25naG83QGRndS5hYy5rciIsImlhdCI6MTczOTIwNDY5NiwiZXhwIjoxNzQxNzk2Njk2fQ.iWwnAhIzse5UwvHpR5uWa2o0HRM5G14ikeAtYf_BDec'; + console.log('작가 accessToken:', AUTHOR_TOKEN); + + localStorage.setItem('accessToken', AUTHOR_TOKEN); + instance.defaults.headers.common.Authorization = `Bearer ${AUTHOR_TOKEN}`; + + const response = await instance.get>( + `/api/mypage` + ); + return response.data.result; +}; diff --git a/src/apis/mypageAuthor/myPage/type.ts b/src/apis/mypageAuthor/myPage/type.ts new file mode 100644 index 0000000..de2330f --- /dev/null +++ b/src/apis/mypageAuthor/myPage/type.ts @@ -0,0 +1,55 @@ +/** + * 작가 마이페이지 조회 API 타입 정의 + * @author 노찬영 + */ + +// 작가 마이페이지 타입 +export type TArtistMypage = { + author: TAuthor; + auctions: TAuction[]; + storage: { + artworks: TArtwork[]; + exhibitions: TExhibition[]; + }; +}; + +// 작가 정보 타입 +export type TAuthor = { + name: string; + profile_image_url: string; +}; + +// 경매 타입 +export type TAuction = { + auction_id: number; + bid_date: string; + bid_price: number; + status: string; + auction: { + artwork_id: number; + artwork: { + title: string; + author: { + author_name: string; + author_image_url: string; + }; + }; + }; +}; + +// 작품 타입 +export type TArtwork = { + id: number; + title: string; + author?: { name: string }; + thumbnail_image_url: string; + height: string; + width: string; +}; + +// 전시 타입 +export type TExhibition = { + id: number; + title: string; + image_url: string; +}; diff --git a/src/apis/mypageAuthor/profile.ts b/src/apis/mypageAuthor/profile.ts new file mode 100644 index 0000000..4db29fc --- /dev/null +++ b/src/apis/mypageAuthor/profile.ts @@ -0,0 +1,22 @@ +import { instance } from '@/apis/axios'; +import { TGetResponse } from '@/apis/type'; +import { TUpdateAuthorProfile } from './type'; + +/** + * 작가 프로필 정보 개별 수정 API 호출 함수 + * @param data - 수정할 속성(attribute)과 값(value) + * @returns void (응답 데이터를 처리하지 않음) + * @author 노찬영 + */ +export const updateAuthorProfile = async ( + data: TUpdateAuthorProfile +): Promise => { + const response = await instance.patch>( + '/api/author/profile', + data + ); + + if (!response.data.isSuccess) { + throw new Error('작가 프로필 수정에 실패했습니다.'); + } +}; diff --git a/src/apis/mypageAuthor/type.ts b/src/apis/mypageAuthor/type.ts new file mode 100644 index 0000000..e6894c8 --- /dev/null +++ b/src/apis/mypageAuthor/type.ts @@ -0,0 +1,84 @@ +export type AuthorProfileType = 'default' | 'intro' | 'info'; + +export interface EducationInfo { + school: string; + major: string; + status: string; + start_date: string; + end_date: string; +} + +export interface AwardInfo { + date: string; + description: string; +} + +export interface ExperienceInfo { + date: string; + description: string; +} + +export type TAuthorProfile = { + isSuccess: boolean; + code: string; + message: string; + author_name: string; + author_image_url: string; + email: string; + description: string; + work_style: string; + education: EducationInfo[]; + award: AwardInfo[]; + experience: ExperienceInfo[]; +}; + +export type TAuthorArtworksExhibitions = { + author: { + id: number; + }; + artworks: { + id: number; + title: string; + thumbnail_image_url: string; + }[]; + auction_artworks: { + auction_id: number; + auction_period: string; + artwork: { + id: number; + title: string; + thumbnail_image_url: string; + }; + }[]; + exhibitions: { + id: number; + title: string; + image_url: string; + }[]; +}; + +export type TSaveBankInfo = { + bank_name: string; + account_holder: string; + account_number: string; +}; + +export type TUpdateAuthorInfo = { + nickname?: string; + address?: string; + birth?: string; + author_image_url?: string; + introduction_image_url?: string; +}; + +export type AuthorProfileAttribute = + | 'description' + | 'work_style' + | 'education' + | 'award' + | 'experience'; + +export type TUpdateAuthorProfile = { + attribute: AuthorProfileAttribute; + value: string; +}; diff --git a/src/apis/mypageBuyer/artBuyer.ts b/src/apis/mypageBuyer/artBuyer.ts new file mode 100644 index 0000000..f0c8603 --- /dev/null +++ b/src/apis/mypageBuyer/artBuyer.ts @@ -0,0 +1,15 @@ +import { instance } from '@/apis/axios'; +import { TGetResponse } from '@/apis/type'; +import { TUpdateUserInfo } from './type'; + +/** + * 사용자 정보 수정 API 호출 함수 + * @param userInfo - 수정할 사용자 정보 (nickname, address, birth) + * @returns API 응답(TGetResponse) + * @author 노찬영, 홍규진 + */ +export const updateUserInfo = async ( + userInfo: TUpdateUserInfo +): Promise => { + await instance.patch>(`/api/user/update`, userInfo); +}; diff --git a/src/apis/mypageBuyer/artWork.ts b/src/apis/mypageBuyer/artWork.ts new file mode 100644 index 0000000..1c04cf8 --- /dev/null +++ b/src/apis/mypageBuyer/artWork.ts @@ -0,0 +1,15 @@ +import { instance } from '@/apis/axios'; +import { TPurchasedArtwork } from './type'; +import { TGetResponse } from '../type'; + +/** + * 구매한 작품 리스트를 조회하는 API 호출 함수 + * @returns 구매한 작품 리스트(TPurchasedArtwork) + * @author 노찬영 + **/ +export const getPurchasedArtworks = async (): Promise => { + const response = await instance.get>( + '/api/user/purchased-artworks' + ); + return response.data.result; +}; diff --git a/src/apis/mypageBuyer/myPage/myPage.ts b/src/apis/mypageBuyer/myPage/myPage.ts new file mode 100644 index 0000000..310751c --- /dev/null +++ b/src/apis/mypageBuyer/myPage/myPage.ts @@ -0,0 +1,24 @@ +import { instance } from '@/apis/axios'; +import { TBuyerMypage } from './type'; +import { TGetResponse } from '@/apis/type'; + +/** + * 작품 구매자의 마이페이지 정보를 조회하는 API 호출 함수 + * @returns 사용자 마이페이지 정보(TBuyerMypage) + * @author 노찬영 + **/ +export const getBuyerMypage = async (): Promise => { + const BUYER_TOKEN = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imh5c29uZzR1QGdtYWlsLmNvbSIsImlhdCI6MTczOTU1MzczMSwiZXhwIjoxNzQyMTQ1NzMxfQ.6ePJhRS1JUNK9BPIOk9oXrYoggois21uBtGsp4gvKrU'; + console.log('구매자 accessToken:', BUYER_TOKEN); + + // 구매자 토큰으로 변경 + localStorage.setItem('accessToken', BUYER_TOKEN); + instance.defaults.headers.common.Authorization = `Bearer ${BUYER_TOKEN}`; + + const response = await instance.get>( + `/api/mypage` + ); + + return response.data.result; +}; diff --git a/src/apis/mypageBuyer/myPage/type.ts b/src/apis/mypageBuyer/myPage/type.ts new file mode 100644 index 0000000..b1994d5 --- /dev/null +++ b/src/apis/mypageBuyer/myPage/type.ts @@ -0,0 +1,76 @@ +/** + * 작품 구매자 마이페이지 조회 API 타입 정의 + * @author 노찬영 + */ + +// 작품 구매자 타입 + +export type TBuyerMypage = { + buyer: { + name: string; + profile_image_url: string; + }; + paymentCounts: TPaymentCount[]; + auctions: TAuction[]; + payments: TPayment[]; + myCollection: { + artworks: TArtwork[]; + exhibitions: TExhibition[]; + }; +}; + +export type TPaymentCount = { + pending: number; + completed: number; + received: number; +}; + +export type TAuction = { + auction_id: number; + bid_date: string; + bid_price: number; + status: string; + auction: { + artwork_id: number; + artwork: { + title: string; + author: { + author_name: string; + author_image_url: string; + }; + }; + }; +}; + +export type TPayment = { + id: number; + auction_id: number; + payment_price: number; + created_at: string; + payment_status: string; + auction: { + artwork_id: number; + artwork: { + title: string; + author: { + author_name: string; + author_image_url: string; + }; + }; + }; +}; + +export type TArtwork = { + id: number; + title: string; + thumbnail_image_url: string; + width?: string; + height?: string; + author?: { author_name: string; author_image_url: string }; +}; + +export type TExhibition = { + id: number; + title: string; + image_url: string; +}; diff --git a/src/apis/mypageBuyer/type.ts b/src/apis/mypageBuyer/type.ts new file mode 100644 index 0000000..59f4166 --- /dev/null +++ b/src/apis/mypageBuyer/type.ts @@ -0,0 +1,18 @@ +/** + * 구매한 작품 데이터 타입 + * @author 노찬영 + **/ + +export interface TPurchasedArtwork { + artworkId: number; // 작품 ID + authorName: string; // 작가 이름 + title: string; // 작품 제목 + size: string; // 작품 크기 + isLiked: boolean; // 좋아요 여부 +} + +export interface TUpdateUserInfo { + nickname?: string; + address?: string; + birth?: string; +} diff --git a/src/apis/register/oAuthLogin.ts b/src/apis/register/oAuthLogin.ts index dd2a3ac..1db6a7b 100644 --- a/src/apis/register/oAuthLogin.ts +++ b/src/apis/register/oAuthLogin.ts @@ -1,6 +1,13 @@ import { instance } from '../axios'; import { TGetResponse } from '../type'; import { TOauthLoginWithCode } from './type'; + +/** + * 소셜 로그인 완료 응답 데이터 타입 + * isComplete: 회원가입 완료 여부 + * token: 인가 토큰 + * @author 홍규진 + */ type TAuthResponse = { isComplete: boolean; token: string; diff --git a/src/apis/register/postAuthRegister.ts b/src/apis/register/postAuthRegister.ts index d539100..51cdd51 100644 --- a/src/apis/register/postAuthRegister.ts +++ b/src/apis/register/postAuthRegister.ts @@ -2,6 +2,12 @@ import { instance } from '@/apis/axios'; import { TGetResponse } from '@/apis/type'; import { TRegisterFormData, TAuthResponse } from './type.ts'; +/** + * 회원가입 완료 API 호출 + * @param authData 회원가입 완료 폼 데이터 + * @returns 회원가입 완료 응답 데이터 + * @author 홍규진 + */ export const postAuthRegister = async ( authData: TRegisterFormData ): Promise => { diff --git a/src/apis/register/type.ts b/src/apis/register/type.ts index a0a7ca3..d342a5d 100644 --- a/src/apis/register/type.ts +++ b/src/apis/register/type.ts @@ -1,3 +1,7 @@ +/** + * 회원가입 폼 데이터 타입 + * @author 홍규진 + */ export type TRegisterFormData = { role: 'BUYER' | 'AUTHOR'; // 사용자 유형 name: string; // 이름 @@ -5,11 +9,19 @@ export type TRegisterFormData = { nickname: string; // 닉네임 }; +/** + * 소셜 로그인 코드 타입 + * @author 홍규진 + */ export type TOauthLoginWithCode = { code: string; social_type: string; }; +/** + * 회원가입 완료 응답 데이터 타입 + * @author 홍규진 + */ export type TAuthResponse = { isComplete: boolean; token: string; From 44e3c8d1b233b006b1730b04bbb414626adfa3de Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:02:27 +0900 Subject: [PATCH 06/11] =?UTF-8?q?Delete:=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EB=AF=B8=EC=A4=80=EC=88=98=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/artBuyerPage/ArtBuyerPage.style.ts | 19 -- src/pages/artBuyerPage/ArtBuyerPage.tsx | 45 ---- .../accountSettings/basicInfo/index.style.ts | 187 ---------------- .../accountSettings/basicInfo/index.tsx | 128 ----------- .../components/accountSettings/index.style.ts | 12 - .../components/accountSettings/index.tsx | 41 ---- .../accountSettings/withdraw/index.style.ts | 48 ---- .../accountSettings/withdraw/index.tsx | 19 -- .../components/menuChooser/index.style.ts | 29 --- .../components/menuChooser/index.tsx | 42 ---- .../menuMyPage/Auction/index.style.ts | 84 ------- .../components/menuMyPage/Auction/index.tsx | 74 ------ .../menuMyPage/MyCollection/index.style.ts | 71 ------ .../menuMyPage/MyCollection/index.tsx | 85 ------- .../menuMyPage/Payment/index.style.ts | 86 ------- .../components/menuMyPage/Payment/index.tsx | 70 ------ .../components/menuMyPage/index.style.ts | 27 --- .../components/menuMyPage/index.tsx | 28 --- .../menuMyPage/profile/index.style.ts | 83 ------- .../components/menuMyPage/profile/index.tsx | 64 ------ .../components/purchasedWorks/index.style.ts | 70 ------ .../components/purchasedWorks/index.tsx | 49 ---- .../artBuyerPage/hooks/useGetBuyerMypage.ts | 37 --- .../hooks/useGetPurchasedArtworks.ts | 36 --- src/pages/artBuyerPage/hooks/useKakaoPay.ts | 51 ----- .../artBuyerPage/hooks/useUpdateUserInfo.ts | 49 ---- src/pages/authorPage/AuthorPage.style.ts | 19 -- src/pages/authorPage/AuthorPage.tsx | 48 ---- .../accountSettings/account/index.style.ts | 77 ------- .../accountSettings/account/index.tsx | 69 ------ .../accountSettings/basicInfo/index.style.ts | 201 ----------------- .../accountSettings/basicInfo/index.tsx | 150 ------------- .../components/accountSettings/index.style.ts | 12 - .../components/accountSettings/index.tsx | 46 ---- .../accountSettings/withdraw/index.style.ts | 48 ---- .../accountSettings/withdraw/index.tsx | 19 -- .../managementWorks/artWorks/index.style.ts | 60 ----- .../managementWorks/artWorks/index.tsx | 51 ----- .../auctioningWorks/index.style.ts | 60 ----- .../managementWorks/auctioningWorks/index.tsx | 49 ---- .../exhibitions/index.style.ts | 60 ----- .../managementWorks/exhibitions/index.tsx | 56 ----- .../components/managementWorks/index.style.ts | 12 - .../components/managementWorks/index.tsx | 45 ---- .../managingProfiles/allInfo/index.style.ts | 134 ----------- .../managingProfiles/allInfo/index.tsx | 147 ------------ .../managingProfiles/index.style.ts | 12 - .../components/managingProfiles/index.tsx | 45 ---- .../managingProfiles/introduce/index.style.ts | 124 ----------- .../managingProfiles/introduce/index.tsx | 52 ----- .../myInformation/index.style.ts | 89 -------- .../managingProfiles/myInformation/index.tsx | 146 ------------ .../components/menuChooser/index.style.ts | 29 --- .../components/menuChooser/index.tsx | 44 ---- .../menuMyPage/Auction/index.style.ts | 61 ----- .../components/menuMyPage/Auction/index.tsx | 62 ------ .../artworkCollection/index.style.ts | 71 ------ .../menuMyPage/artworkCollection/index.tsx | 61 ----- .../components/menuMyPage/index.style.ts | 27 --- .../components/menuMyPage/index.tsx | 30 --- .../menuMyPage/profile/index.style.ts | 82 ------- .../components/menuMyPage/profile/index.tsx | 45 ---- .../hooks/useGetAuthorArtworksExhibitions.ts | 35 --- .../authorPage/hooks/useGetAuthorMypage.ts | 37 --- .../authorPage/hooks/useGetAuthorProfile.ts | 46 ---- .../authorPage/hooks/useSaveAuthorBankInfo.ts | 24 -- .../authorPage/hooks/useUpdateAuthorInfo.ts | 43 ---- .../hooks/useUpdateAuthorProfile.ts | 38 ---- .../components/StepOne/index.style.ts | 65 ------ .../components/StepOne/index.tsx | 110 --------- .../components/StepThree/index.style.ts | 65 ------ .../components/StepThree/index.tsx | 210 ------------------ .../components/StepTwo/index.style.ts | 66 ------ .../components/StepTwo/index.tsx | 73 ------ .../hooks/useExhibitRegister.ts | 85 ------- .../hooks/useGetExhibitAvailableArtwork.ts | 21 -- .../hooks/useGetExhibitBackground.ts | 21 -- src/pages/exhibit-register/index.style.ts | 50 ----- src/pages/exhibit-register/index.tsx | 75 ------- .../GalleryBox/index.style.ts | 16 -- .../exhibition-detail/GalleryBox/index.tsx | 16 -- 81 files changed, 4973 deletions(-) delete mode 100644 src/pages/artBuyerPage/ArtBuyerPage.style.ts delete mode 100644 src/pages/artBuyerPage/ArtBuyerPage.tsx delete mode 100644 src/pages/artBuyerPage/components/accountSettings/basicInfo/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/accountSettings/basicInfo/index.tsx delete mode 100644 src/pages/artBuyerPage/components/accountSettings/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/accountSettings/index.tsx delete mode 100644 src/pages/artBuyerPage/components/accountSettings/withdraw/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/accountSettings/withdraw/index.tsx delete mode 100644 src/pages/artBuyerPage/components/menuChooser/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/menuChooser/index.tsx delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/Auction/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/Auction/index.tsx delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.tsx delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/Payment/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/Payment/index.tsx delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/index.tsx delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/profile/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/menuMyPage/profile/index.tsx delete mode 100644 src/pages/artBuyerPage/components/purchasedWorks/index.style.ts delete mode 100644 src/pages/artBuyerPage/components/purchasedWorks/index.tsx delete mode 100644 src/pages/artBuyerPage/hooks/useGetBuyerMypage.ts delete mode 100644 src/pages/artBuyerPage/hooks/useGetPurchasedArtworks.ts delete mode 100644 src/pages/artBuyerPage/hooks/useKakaoPay.ts delete mode 100644 src/pages/artBuyerPage/hooks/useUpdateUserInfo.ts delete mode 100644 src/pages/authorPage/AuthorPage.style.ts delete mode 100644 src/pages/authorPage/AuthorPage.tsx delete mode 100644 src/pages/authorPage/components/accountSettings/account/index.style.ts delete mode 100644 src/pages/authorPage/components/accountSettings/account/index.tsx delete mode 100644 src/pages/authorPage/components/accountSettings/basicInfo/index.style.ts delete mode 100644 src/pages/authorPage/components/accountSettings/basicInfo/index.tsx delete mode 100644 src/pages/authorPage/components/accountSettings/index.style.ts delete mode 100644 src/pages/authorPage/components/accountSettings/index.tsx delete mode 100644 src/pages/authorPage/components/accountSettings/withdraw/index.style.ts delete mode 100644 src/pages/authorPage/components/accountSettings/withdraw/index.tsx delete mode 100644 src/pages/authorPage/components/managementWorks/artWorks/index.style.ts delete mode 100644 src/pages/authorPage/components/managementWorks/artWorks/index.tsx delete mode 100644 src/pages/authorPage/components/managementWorks/auctioningWorks/index.style.ts delete mode 100644 src/pages/authorPage/components/managementWorks/auctioningWorks/index.tsx delete mode 100644 src/pages/authorPage/components/managementWorks/exhibitions/index.style.ts delete mode 100644 src/pages/authorPage/components/managementWorks/exhibitions/index.tsx delete mode 100644 src/pages/authorPage/components/managementWorks/index.style.ts delete mode 100644 src/pages/authorPage/components/managementWorks/index.tsx delete mode 100644 src/pages/authorPage/components/managingProfiles/allInfo/index.style.ts delete mode 100644 src/pages/authorPage/components/managingProfiles/allInfo/index.tsx delete mode 100644 src/pages/authorPage/components/managingProfiles/index.style.ts delete mode 100644 src/pages/authorPage/components/managingProfiles/index.tsx delete mode 100644 src/pages/authorPage/components/managingProfiles/introduce/index.style.ts delete mode 100644 src/pages/authorPage/components/managingProfiles/introduce/index.tsx delete mode 100644 src/pages/authorPage/components/managingProfiles/myInformation/index.style.ts delete mode 100644 src/pages/authorPage/components/managingProfiles/myInformation/index.tsx delete mode 100644 src/pages/authorPage/components/menuChooser/index.style.ts delete mode 100644 src/pages/authorPage/components/menuChooser/index.tsx delete mode 100644 src/pages/authorPage/components/menuMyPage/Auction/index.style.ts delete mode 100644 src/pages/authorPage/components/menuMyPage/Auction/index.tsx delete mode 100644 src/pages/authorPage/components/menuMyPage/artworkCollection/index.style.ts delete mode 100644 src/pages/authorPage/components/menuMyPage/artworkCollection/index.tsx delete mode 100644 src/pages/authorPage/components/menuMyPage/index.style.ts delete mode 100644 src/pages/authorPage/components/menuMyPage/index.tsx delete mode 100644 src/pages/authorPage/components/menuMyPage/profile/index.style.ts delete mode 100644 src/pages/authorPage/components/menuMyPage/profile/index.tsx delete mode 100644 src/pages/authorPage/hooks/useGetAuthorArtworksExhibitions.ts delete mode 100644 src/pages/authorPage/hooks/useGetAuthorMypage.ts delete mode 100644 src/pages/authorPage/hooks/useGetAuthorProfile.ts delete mode 100644 src/pages/authorPage/hooks/useSaveAuthorBankInfo.ts delete mode 100644 src/pages/authorPage/hooks/useUpdateAuthorInfo.ts delete mode 100644 src/pages/authorPage/hooks/useUpdateAuthorProfile.ts delete mode 100644 src/pages/exhibit-register/components/StepOne/index.style.ts delete mode 100644 src/pages/exhibit-register/components/StepOne/index.tsx delete mode 100644 src/pages/exhibit-register/components/StepThree/index.style.ts delete mode 100644 src/pages/exhibit-register/components/StepThree/index.tsx delete mode 100644 src/pages/exhibit-register/components/StepTwo/index.style.ts delete mode 100644 src/pages/exhibit-register/components/StepTwo/index.tsx delete mode 100644 src/pages/exhibit-register/hooks/useExhibitRegister.ts delete mode 100644 src/pages/exhibit-register/hooks/useGetExhibitAvailableArtwork.ts delete mode 100644 src/pages/exhibit-register/hooks/useGetExhibitBackground.ts delete mode 100644 src/pages/exhibit-register/index.style.ts delete mode 100644 src/pages/exhibit-register/index.tsx delete mode 100644 src/pages/exhibition-detail/GalleryBox/index.style.ts delete mode 100644 src/pages/exhibition-detail/GalleryBox/index.tsx diff --git a/src/pages/artBuyerPage/ArtBuyerPage.style.ts b/src/pages/artBuyerPage/ArtBuyerPage.style.ts deleted file mode 100644 index 9f2512a..0000000 --- a/src/pages/artBuyerPage/ArtBuyerPage.style.ts +++ /dev/null @@ -1,19 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const MyPageWrapper = styled.div` - display: flex; - flex-direction: column; - width: auto; - margin: 0 245px auto; - align-items: flex-end; - margin-bottom: 200px; - background-color: ${theme.colors.white}; -`; - -export const MenuWrapper = styled.div` - display: flex; - justify-content: flex-end; - background-color: ${theme.colors.white}; - padding: 80px 220px 100px 0; -`; diff --git a/src/pages/artBuyerPage/ArtBuyerPage.tsx b/src/pages/artBuyerPage/ArtBuyerPage.tsx deleted file mode 100644 index 6ab7d28..0000000 --- a/src/pages/artBuyerPage/ArtBuyerPage.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useState } from 'react'; -import { MenuChooser } from './components/menuChooser'; -import { PageLayout } from '@/components/common/PageLayout'; - -import { MenuMyPage } from './components/menuMyPage'; -import AccountSettings from './components/accountSettings'; -import PurchasedWorks from './components/purchasedWorks'; - -import { MyPageWrapper, MenuWrapper } from './ArtBuyerPage.style'; - -export default function ArtBuyerPage() { - const [selectedMenu, setSelectedMenu] = useState< - '마이페이지' | '계정설정' | '구매 작품' - >('마이페이지'); - - // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 - const renderSelectedMenu = () => { - switch (selectedMenu) { - case '마이페이지': - return ; - case '계정설정': - return ; - case '구매 작품': - return ; - default: - return null; - } - }; - - return ( - - {/* 메뉴 선택 컴포넌트 */} - - - - - {/* 선택된 메뉴에 따라 컴포넌트 변경 */} - {renderSelectedMenu()} - - ); -} \ No newline at end of file diff --git a/src/pages/artBuyerPage/components/accountSettings/basicInfo/index.style.ts b/src/pages/artBuyerPage/components/accountSettings/basicInfo/index.style.ts deleted file mode 100644 index 39ea986..0000000 --- a/src/pages/artBuyerPage/components/accountSettings/basicInfo/index.style.ts +++ /dev/null @@ -1,187 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - width: 1080px; - margin: 0 auto; - background-color: ${theme.colors.white}; - - h1 { - margin: 0; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - padding-bottom: 20px; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const SectionTitle = styled.span` - ${theme.typography['20']} - font-weight: 600; - color: ${theme.colors.black}; -`; - -export const AccountInfo = styled.section` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 38px; - padding: 64px 0 48px 140px; - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const ProfileImageContainer = styled.div` - position: relative; - - width: 100px; - height: 100px; - - svg { - position: absolute; - right: 0; - bottom: 0; - } -`; - -export const ProfileImage = styled.img` - width: 100px; - height: 100px; - border-radius: 50%; - background-color: ${theme.colors.lightGray}; -`; - -export const UserDetails = styled.div` - display: flex; - flex-direction: column; - gap: 32px; - margin-top: calc(50px - 38px); - - div { - display: flex; - width: 257px; - height: 20px; - gap: 17px; - align-items: center; - } - - span { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.black}; - } -`; - -export const InputContainer = styled.section` - display: flex; - width: 800px; - height: 432px; - flex-direction: column; - align-items: flex-start; - gap: 20px; - padding: 64px 0 50px 140px; - border-bottom: 1px solid ${theme.colors.lineLightColor}; - - label { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - } -`; - -export const BasicField = styled.div` - display: flex; - align-items: center; - gap: 17px; - - .name { - width: 120px; - margin-bottom: 12px; - - ${theme.typography['14']} - font-weight: 400; - } - - span { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - } -`; - -export const InputField = styled.div` - display: flex; - align-items: center; - gap: 17px; - - input { - width: 629px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } - - .address-container { - display: flex; - flex-direction: column; - gap: 10px; - - .primary-address { - display: flex; - align-items: center; - gap: 10px; - - input { - width: 512px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } - } - - input:last-child { - width: 631px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } - } -`; - -export const AddressButton = styled.button` - display: flex; - justify-content: center; - align-items: center; - width: 107px; - padding: 14px 16px; - ${theme.typography['16']} - font-weight: 600; - cursor: pointer; - background-color: ${theme.colors.white}; - border: 1px solid ${theme.colors.black}; -`; - -export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` - width: 106px; - padding: 12px 16px; - font-size: 14px; - font-weight: bold; - cursor: pointer; - border: ${({ variant }) => - variant === 'white' ? `1px solid ${theme.colors.lightGray}` : 'none'}; - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.white}; - color: ${({ variant }) => - variant === 'black' ? theme.colors.white : theme.colors.black}; -`; - -export const ButtonContainer = styled.div` - display: flex; - justify-content: center; - gap: 8px; - margin-top: 48px; -`; diff --git a/src/pages/artBuyerPage/components/accountSettings/basicInfo/index.tsx b/src/pages/artBuyerPage/components/accountSettings/basicInfo/index.tsx deleted file mode 100644 index 7a1a94c..0000000 --- a/src/pages/artBuyerPage/components/accountSettings/basicInfo/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useState } from 'react'; -import { - AccountInfo, - AddressButton, - BasicField, - ButtonContainer, - FormContainer, - InputContainer, - InputField, - ProfileImage, - ProfileImageContainer, - SectionTitle, - StyledButton, - UserDetails, -} from './index.style'; -import DefaultImage from '@assets/svg/Icon_Profile.svg'; -import EditButton from '@assets/svg/Icon_Edit.svg?react'; - -import { useGetBuyerMypage } from '@/pages/artBuyerPage/hooks/useGetBuyerMypage'; -import { useUpdateUserInfo } from '@/pages/artBuyerPage/hooks/useUpdateUserInfo'; - -export const BasicInfo = () => { - const { userMypageData } = useGetBuyerMypage(); - - // 사용자 데이터 확인 - const buyer = userMypageData.buyer; - - // TODO[찬영] - 로그인 API Response 데이터 연결 - const [nickname, setNickname] = useState(''); - const [birth, setBirth] = useState(''); - const [address, setAddress] = useState(''); - const [isSaved, setIsSaved] = useState(false); - - const { updateUser, isPending } = useUpdateUserInfo(); - - const handleSave = () => { - updateUser( - { nickname, birth, address }, - { - onSuccess: () => { - setIsSaved(true); - }, - } - ); - }; - - return ( - -

계정 설정

- - - 계정 정보 - - - - - -
- 닉네임 - {nickname} -
-
- 이메일 - artné@gmail.com -
-
- 타입 - 작품 구매자 -
-
-
- - - 기본 정보 - -
이름
-
{buyer?.name || '사용자 이름'}
-
- - 휴대전화 - 010-1234-1234 - - - - setNickname(e.target.value)} - /> - - - - setBirth(e.target.value)} - /> - - - -
-
- setAddress(e.target.value)} - /> - 주소찾기 -
- -
-
-
- - - 취소 - - {isPending ? '저장 중...' : '저장하기'} - - {isSaved && ( - 저장 완료! - )} - -
- ); -}; diff --git a/src/pages/artBuyerPage/components/accountSettings/index.style.ts b/src/pages/artBuyerPage/components/accountSettings/index.style.ts deleted file mode 100644 index d1b1dc6..0000000 --- a/src/pages/artBuyerPage/components/accountSettings/index.style.ts +++ /dev/null @@ -1,12 +0,0 @@ -import styled from '@emotion/styled'; - -export const TabBoxWrapper = styled.div` - display: flex; - margin-right: 100px; -`; - -export const AccountSettingWrapper = styled.div` - display: flex; - justify-content: center; - width: 100%; -`; diff --git a/src/pages/artBuyerPage/components/accountSettings/index.tsx b/src/pages/artBuyerPage/components/accountSettings/index.tsx deleted file mode 100644 index 7d6bf6e..0000000 --- a/src/pages/artBuyerPage/components/accountSettings/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useState } from 'react'; -import { BasicInfo } from './basicInfo'; -import { Withdraw } from './withdraw'; -import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; -import { MyPageSideBar } from '@/components/common/MyPageSideBar'; - -export const AccountSettings = () => { - const [selectedTab, setSelectedTab] = useState<'기본정보 관리' | '회원 탈퇴'>( - '기본정보 관리' - ); - - // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 - const renderSelectedTab = () => { - switch (selectedTab) { - case '기본정보 관리': - return ; - case '회원 탈퇴': - return ; - default: - return null; - } - }; - - return ( - - {/* 메뉴 선택 컴포넌트 */} - - - - - {/* 선택된 메뉴에 따라 컴포넌트 변경 */} - {renderSelectedTab()} - - ); -}; - -export default AccountSettings; diff --git a/src/pages/artBuyerPage/components/accountSettings/withdraw/index.style.ts b/src/pages/artBuyerPage/components/accountSettings/withdraw/index.style.ts deleted file mode 100644 index e68f23b..0000000 --- a/src/pages/artBuyerPage/components/accountSettings/withdraw/index.style.ts +++ /dev/null @@ -1,48 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - width: 1080px; - height: 56px; - background-color: ${theme.colors.white}; -`; - -export const SectionTitle = styled.h1` - margin: 0; - ${theme.typography['24']} - font-weight: 600; - padding-bottom: 20px; - border-bottom: 2px solid ${theme.colors.black}; -`; - -export const ButtonContainer = styled.div` - display: flex; - width: 100%; - padding: 48px 0 104px; - justify-content: center; - margin-top: 315px; - - gap: 8px; - - border-top: 1px solid ${theme.colors.lineLightColor}; - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` - width: 106px; - padding: 10px 20px; - font-size: 14px; - font-weight: bold; - cursor: pointer; - border: ${({ variant }) => - variant === 'white' ? `1px solid ${theme.colors.black}` : 'none'}; - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.white}; - color: ${({ variant }) => - variant === 'black' ? theme.colors.white : theme.colors.black}; - - &:hover { - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.lightGray}; - } -`; diff --git a/src/pages/artBuyerPage/components/accountSettings/withdraw/index.tsx b/src/pages/artBuyerPage/components/accountSettings/withdraw/index.tsx deleted file mode 100644 index a3cf8bd..0000000 --- a/src/pages/artBuyerPage/components/accountSettings/withdraw/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - ButtonContainer, - FormContainer, - SectionTitle, - StyledButton, -} from './index.style'; - -export const Withdraw = () => { - return ( - - 회원 탈퇴 - - - 취소 - 탈퇴하기 - - - ); -}; diff --git a/src/pages/artBuyerPage/components/menuChooser/index.style.ts b/src/pages/artBuyerPage/components/menuChooser/index.style.ts deleted file mode 100644 index a5f1977..0000000 --- a/src/pages/artBuyerPage/components/menuChooser/index.style.ts +++ /dev/null @@ -1,29 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@styles/theme.ts'; - -export const Wrapper = styled.div` - display: flex; - width: 1080px; - align-items: center; - - background-color: ${theme.colors.white}; - border: 1px solid ${theme.colors.lineLightColor}; -`; - -export const MenuButton = styled.button<{ $isActive: boolean }>` - width: 100%; - padding: 16px; - border: none; - &:hover { - cursor: pointer; - } - - ${theme.typography['16']} - text-align: center; - text-overflow: ellipsis; - background-color: ${({ $isActive, theme }) => - $isActive ? theme.colors.black : 'transparent'}; - color: ${({ $isActive, theme }) => - $isActive ? theme.colors.white : theme.colors.font03gray}; - transition: background-color 0.2s ease, color 0.2s ease; -`; diff --git a/src/pages/artBuyerPage/components/menuChooser/index.tsx b/src/pages/artBuyerPage/components/menuChooser/index.tsx deleted file mode 100644 index 180b10a..0000000 --- a/src/pages/artBuyerPage/components/menuChooser/index.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Wrapper, MenuButton } from './index.style'; - -interface MenuChooserProps { - menus: T[]; - selectedMenu: T; - onSelectMenu: (menu: T) => void; -} - -/** - * 마이페이지 내 메뉴 선택 컴포넌트입니다. - * `menus`를 props로 받아 다른 페이지에서도 활용 가능하도록 작성하였습니다. - * 컴포넌트 타입을 제네릭으로 설정해 확장성과 타입 안전성을 높였습니다. - * @param {string[]} menus - 메뉴 목록 - * @param {string} selectedMenu - 현재 선택된 메뉴 - * @param {(menu: string) => void} onSelectMenu - 선택된 메뉴를 부모 컴포넌트에 전달하는 콜백 함수 - * - * @example - * @author 노찬영 - */ -export const MenuChooser = ({ - menus, - selectedMenu, - onSelectMenu, -}: MenuChooserProps) => { - return ( - - {menus?.map((menu) => ( - onSelectMenu(menu)} - > - {menu} - - ))} - - ); -}; diff --git a/src/pages/artBuyerPage/components/menuMyPage/Auction/index.style.ts b/src/pages/artBuyerPage/components/menuMyPage/Auction/index.style.ts deleted file mode 100644 index b714e90..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/Auction/index.style.ts +++ /dev/null @@ -1,84 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const AuctionContainer = styled.div` - display: flex; - width: 1080px; - flex-direction: column; - align-items: flex-start; - color: ${theme.colors.black}; - - h1 { - ${theme.typography['24']} - font-weight: 600; - } -`; - -export const Table = styled.table` - width: 100%; - border-collapse: collapse; - margin-top: 4px; - ${theme.typography['14']} - font-weight: 400; - padding: 22px 16px; - border-top: 2px solid ${theme.colors.black}; -`; - -export const TableHeader = styled.th` - padding: 22px 16px; - border-bottom: 1px solid ${theme.colors.fontGray}; - text-align: center; - ${theme.typography['14']} - font-weight: 600; - - &:nth-of-type(1) { - width: 630px; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4) { - width: 150px; - } -`; - -export const TableRow = styled.tr` - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const TableCell = styled.td` - &:nth-of-type(1) { - text-align: left; - padding: 22px 16px; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4), - &:nth-of-type(5) { - text-align: center; - padding: 22px 16px; - } - - &:nth-of-type(5) { - width: 76px; - padding: 19px 8px; - text-align: center; - } -`; - -export const AuctionButton = styled.button` - width: 100%; - display: flex; - padding: 7px 6px; - justify-content: center; - align-items: center; - - border: none; - border-radius: 2px; - cursor: pointer; - background-color: ${theme.colors.font03gray}; - color: ${theme.colors.white}; - ${theme.typography['14']} - font-weight: 400; -`; diff --git a/src/pages/artBuyerPage/components/menuMyPage/Auction/index.tsx b/src/pages/artBuyerPage/components/menuMyPage/Auction/index.tsx deleted file mode 100644 index 87249cd..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/Auction/index.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { - AuctionButton, - AuctionContainer, - Table, - TableCell, - TableHeader, - TableRow, -} from './index.style'; -import { useNavigate } from 'react-router-dom'; -import { useGetBuyerMypage } from '@/pages/artBuyerPage/hooks/useGetBuyerMypage'; - -/** - * @description 작품 구매자의 경매 내역을 표시하는 컴포넌트 - * @author 노찬영 - */ -export const ArtBuyerAuction = () => { - const { userMypageData } = useGetBuyerMypage(); - const navigate = useNavigate(); - - const { auctions } = userMypageData; - - const handleBtnClick = (auctionId: number) => { - navigate(`/auction/${auctionId}`); - }; - - // status 값을 한글로 변환하는 함수 - const getStatusText = (status: string) => { - const statusMap: Record = { - BID: '경매완료', - PARTICIPATE: '입찰중', - }; - return statusMap[status] || status; // 기본적으로 원래 상태값 유지 - }; - - return ( - -

경매

- - - - 작품정보 - 경매 종료일 - 금액 - 진행상황 - - - - - {auctions?.map((auction) => ( - - {`${auction.auction.artwork.title} - ${auction.auction.artwork.author.author_name}`} - - {new Date(auction.bid_date).toLocaleDateString('ko-KR')} - - {`₩${auction.bid_price.toLocaleString()}`} - {getStatusText(auction.status)} - - {auction.status === 'PARTICIPATE' && ( - handleBtnClick(auction.auction_id)} - > - 입찰하기 - - )} - - - ))} - -
-
- ); -}; - -export default ArtBuyerAuction; diff --git a/src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.style.ts b/src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.style.ts deleted file mode 100644 index 5b6e6c8..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.style.ts +++ /dev/null @@ -1,71 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const MyCollectionContainer = styled.div` - width: 1080px; - height: auto; - display: flex; - flex-direction: column; - gap: 32px; - color: ${theme.colors.black}; - - h1 { - margin: 0; - padding: 70px 0 20px; - ${theme.typography['24']} - font-weight: 600; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const ArtworkContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const SectionTitle = styled.h2` - margin: 0; - ${theme.typography['20']} - font-weight: 600; -`; - -export const ArtworkGrid = styled.div` - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 40px 20px; - padding: 18px 0 64px; - border-bottom: 1px solid ${theme.colors.borderBottom}; -`; - -export const ExhibitionContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const ExhibitionGrid = styled.div` - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 76px 21px; - margin: 19px 0 80px; -`; - -export const ExhibitionItem = styled.div` - width: 346px; - height: 260px; - - h3 { - color: ${theme.colors.black}; - ${theme.typography['14']} - font-weight: 600; - } -`; - -export const ExhibitionImage = styled.img` - width: 346px; - height: 260px; - object-fit: cover; -`; diff --git a/src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.tsx b/src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.tsx deleted file mode 100644 index f2b88da..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/MyCollection/index.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { - MyCollectionContainer, - SectionTitle, - ArtworkGrid, - ExhibitionGrid, - ExhibitionItem, - ExhibitionImage, - ArtworkContainer, - ExhibitionContainer, -} from './index.style'; - -import { Artwork } from '@/components/common/ArtWork'; - -import { useNavigate } from 'react-router-dom'; -import { useGetBuyerMypage } from '@/pages/artBuyerPage/hooks/useGetBuyerMypage'; - -/** - * @description 작품 구매자의 작품과 전시를 표시하는 컴포넌트 - * @author 노찬영 - */ -const MyCollection = () => { - const navigate = useNavigate(); - const { userMypageData } = useGetBuyerMypage(); - - // 작품 구매자 작품 데이터 - const artworks = userMypageData?.myCollection?.artworks || []; - // 작품 구매자 전시 데이터 - const exhibitions = userMypageData?.myCollection?.exhibitions || []; - - // 작품 클릭 시 작품 상세 페이지로 이동 - const handleArtworkClick = (artworkId: number) => { - navigate(`/artwork/${artworkId}`); - }; - - // 전시 클릭 시 전시 상세 페이지로 이동 - const handleExhibitionClick = (exhibitionId: number) => { - navigate(`/exhibition/${exhibitionId}`); - }; - - return ( - -

마이컬렉션

- - - 작품 - - {artworks.map((artwork) => ( - handleArtworkClick(artwork.id)} - /> - ))} - - - - - 전시 - - {exhibitions.map((exhibition) => ( - handleExhibitionClick(exhibition.id)} - style={{ cursor: 'pointer' }} - > - -

{exhibition.title}

-
- ))} -
-
-
- ); -}; - -export default MyCollection; diff --git a/src/pages/artBuyerPage/components/menuMyPage/Payment/index.style.ts b/src/pages/artBuyerPage/components/menuMyPage/Payment/index.style.ts deleted file mode 100644 index 1a6471b..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/Payment/index.style.ts +++ /dev/null @@ -1,86 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const PaymentContainer = styled.div` - display: flex; - width: 1080px; - flex-direction: column; - align-items: flex-start; - color: ${theme.colors.black}; - - h1 { - ${theme.typography['24']} - font-weight: 600; - } -`; - -export const Table = styled.table` - width: 100%; - border-collapse: collapse; - margin-top: 4px; - ${theme.typography['14']} - font-weight: 400; - padding: 22px 16px; - border-top: 2px solid ${theme.colors.black}; -`; - -export const TableHeader = styled.th` - border-bottom: 1px solid ${theme.colors.fontGray}; - text-align: center; - ${theme.typography['14']} - font-weight: 600; - padding: 22px 16px; - - &:nth-of-type(1) { - width: 630px; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4) { - width: 150px; - } - &:nth-of-type(5) { - width: 76px; - text-align: center; - } -`; - -export const TableRow = styled.tr` - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const TableCell = styled.td` - &:nth-of-type(1) { - padding: 22px 16px; - text-align: left; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4) { - padding: 22px 16px; - text-align: center; - } - - &:nth-of-type(5) { - padding: 19px 8px; - text-align: center; - } -`; - -export const PaymentButton = styled.button` - width: 100%; - display: flex; - padding: 7px 6px; - justify-content: center; - align-items: center; - - border: none; - border-radius: 2px; - cursor: pointer; - background-color: ${theme.colors.font03gray}; - color: ${theme.colors.white}; - ${theme.typography['14']} - font-weight: 400; -`; diff --git a/src/pages/artBuyerPage/components/menuMyPage/Payment/index.tsx b/src/pages/artBuyerPage/components/menuMyPage/Payment/index.tsx deleted file mode 100644 index dd494cd..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/Payment/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { - PaymentContainer, - Table, - TableHeader, - TableRow, - TableCell, - PaymentButton, -} from './index.style'; - -import { useGetBuyerMypage } from '@/pages/artBuyerPage/hooks/useGetBuyerMypage'; -import { useKakaoPay } from '@/pages/artBuyerPage/hooks/useKakaoPay'; - -/** - * @description 작품 구매자의 결제 내역을 표시하는 컴포넌트 - * @author 노찬영 - */ -export const Payment = () => { - const { userMypageData } = useGetBuyerMypage(); - const { initiatePayment } = useKakaoPay(); - - const { payments } = userMypageData; - - const handleBtnClick = (paymentId: number) => { - initiatePayment(paymentId); - }; - - const getPaymentStatus = (status: string) => { - if (status === 'COMPLETED') return '결제 완료'; - if (status === 'PENDING') return '결제 대기중'; - return status; - }; - - return ( - -

결제

- - - - 작품정보 - 금액 - 입찰정보 - 진행상황 - - - - - {payments?.map((payment) => ( - - {`${payment.auction.artwork.title} - ${payment.auction.artwork.author.author_name}`} - {`₩${payment.payment_price.toLocaleString()}`} - - {new Date(payment.created_at).toLocaleDateString('ko-KR')} - - {getPaymentStatus(payment.payment_status)} - - {payment.payment_status === 'PENDING' && ( - handleBtnClick(payment.id)}> - 결제하기 - - )} - - - ))} - -
-
- ); -}; - -export default Payment; diff --git a/src/pages/artBuyerPage/components/menuMyPage/index.style.ts b/src/pages/artBuyerPage/components/menuMyPage/index.style.ts deleted file mode 100644 index c290eaf..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/index.style.ts +++ /dev/null @@ -1,27 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const MyPageWrapper = styled.div` - display: flex; - justify-content: center; - width: 100%; - background-color: ${theme.colors.white}; -`; - -export const ProfileContainer = styled.div` - display: flex; - flex-direction: column; - width: 250px; - margin-right: 100px; - background-color: ${theme.colors.white}; -`; - -export const MyPageContainer = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; - max-width: 1080px; - gap: 80px; - background-color: ${theme.colors.white}; -`; - diff --git a/src/pages/artBuyerPage/components/menuMyPage/index.tsx b/src/pages/artBuyerPage/components/menuMyPage/index.tsx deleted file mode 100644 index 4c1aed7..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import ArtBuyerAuction from './Auction'; -import MyCollection from './MyCollection'; -import Payment from './Payment'; -import ArtBuyerProfile from './profile'; -import { - MyPageContainer, - MyPageWrapper, - ProfileContainer, -} from './index.style'; - -interface MenuMyPageProps { - setSelectedMenu: (menu: '마이페이지' | '계정설정' | '구매 작품') => void; -} - -export const MenuMyPage = ({ setSelectedMenu }: MenuMyPageProps) => { - return ( - - - setSelectedMenu('계정설정')} /> - - - - - - - - ); -}; diff --git a/src/pages/artBuyerPage/components/menuMyPage/profile/index.style.ts b/src/pages/artBuyerPage/components/menuMyPage/profile/index.style.ts deleted file mode 100644 index 14a4191..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/profile/index.style.ts +++ /dev/null @@ -1,83 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const ProfileContainer = styled.div` - display: flex; - width: 210px; - height: 335px; - padding: 28px 20px; - flex-direction: column; - justify-content: center; - align-items: center; - - border: 1px solid ${theme.colors.profileBox}; -`; - -export const ProfileImage = styled.img` - width: 80px; - height: 80px; - border-radius: 50%; - object-fit: fill; -`; - -export const ProfileInfo = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-top: 16px; -`; - -export const Name = styled.span` - ${(theme) => theme.theme.typography['18']} - font-weight: 600; - color: ${theme.colors.black}; -`; - -export const EditButton = styled.button` - display: flex; - width: 180px; - padding: 10px 20px; - justify-content: space-between; - align-items: center; - margin-top: 16px; - border-radius: 100px; - border: none; - cursor: pointer; - - ${theme.typography['13']} - font-weight: 400; - background-color: ${theme.colors.profileButton}; - color: ${theme.colors.font03gray}; -`; - -export const PaymentStatus = styled.div` - display: flex; - width: 202px; - flex-direction: column; - align-items: flex-start; - padding: 28px 4px 0; - gap: 20px; - margin-top: 28px; - border-top: 1px solid ${theme.colors.profileBox}; -`; - -export const PaymentItem = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - width: 202px; - height: 20px; -`; - -export const PaymentLabel = styled.span` - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.black}; -`; - -export const PaymentCount = styled.span` - text-align: right; - ${theme.typography['14']} - font-weight: 500; - color: ${theme.colors.black}; -`; diff --git a/src/pages/artBuyerPage/components/menuMyPage/profile/index.tsx b/src/pages/artBuyerPage/components/menuMyPage/profile/index.tsx deleted file mode 100644 index 6572b2b..0000000 --- a/src/pages/artBuyerPage/components/menuMyPage/profile/index.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { - Name, - ProfileContainer, - ProfileImage, - ProfileInfo, - EditButton, - PaymentStatus, - PaymentItem, - PaymentLabel, - PaymentCount, -} from './index.style'; -import RightArrow from '@assets/svg/right-arrow.svg?react'; -import NoneProfile from '@assets/svg/Icon_Profile.svg'; - -import { useGetBuyerMypage } from '@/pages/artBuyerPage/hooks/useGetBuyerMypage'; - -interface ArtBuyerProfileProps { - onEditProfile: () => void; -} - -/** - * 작품 구매자 프로필 컴포넌트입니다. - * 프로필 사진, 본인 이름, 기본정보 수정 버튼, 결제 대기/완료/수령 상태를 표시합니다. - * @param {() => void} onEditProfile - 작품 구매자 계정 정보 수정 - * @author 노찬영 - **/ - -export const ArtBuyerProfile = ({ onEditProfile }: ArtBuyerProfileProps) => { - const { userMypageData } = useGetBuyerMypage(); - - const { buyer, paymentCounts } = userMypageData; - const paymentData = paymentCounts?.[0] ?? { pending: 0, completed: 0, received: 0 }; - - return ( - - - - {buyer.name} - - 기본정보 수정 - - - - - 결제 대기중 - {paymentData.pending} - - - 결제 완료 - {paymentData.completed} - - - 수령 완료 - {paymentData.received} - - - - ); -}; - -export default ArtBuyerProfile; diff --git a/src/pages/artBuyerPage/components/purchasedWorks/index.style.ts b/src/pages/artBuyerPage/components/purchasedWorks/index.style.ts deleted file mode 100644 index a06bcf6..0000000 --- a/src/pages/artBuyerPage/components/purchasedWorks/index.style.ts +++ /dev/null @@ -1,70 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const PurchasedWorksContainer = styled.div` - width: 1080px; - height: auto; - display: flex; - flex-direction: column; - gap: 32px; - color: ${theme.colors.black}; - - h1 { - margin: 0; - padding: 70px 0 20px; - ${theme.typography['24']} - font-weight: 600; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const ArtworkContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const SectionTitle = styled.h2` - margin: 0; - ${theme.typography['20']} - font-weight: 600; -`; - -export const ArtworkGrid = styled.div` - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 40px 20px; - padding: 18px 0 64px; -`; - -export const ExhibitionContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const ExhibitionGrid = styled.div` - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 76px 21px; - margin: 19px 0 80px; -`; - -export const ExhibitionItem = styled.div` - width: 346px; - height: 260px; - - h3 { - color: ${theme.colors.black}; - ${theme.typography['14']} - font-weight: 600; - } -`; - -export const ExhibitionImage = styled.img` - width: 346px; - height: 260px; - object-fit: cover; -`; diff --git a/src/pages/artBuyerPage/components/purchasedWorks/index.tsx b/src/pages/artBuyerPage/components/purchasedWorks/index.tsx deleted file mode 100644 index dc4cb0b..0000000 --- a/src/pages/artBuyerPage/components/purchasedWorks/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { - PurchasedWorksContainer, - SectionTitle, - ArtworkGrid, - ArtworkContainer, -} from './index.style'; - -import { Artwork } from '@/components/common/ArtWork'; - -import { useGetBuyerMypage } from '../../hooks/useGetBuyerMypage'; -import { useNavigate } from 'react-router-dom'; - -const PurchasedWorks = () => { - const navigate = useNavigate(); - // 작품 클릭 시 작품 상세 페이지로 이동 - const handleArtworkClick = (artworkId: number) => { - navigate(`/artwork/${artworkId}`); - }; - - const { userMypageData } = useGetBuyerMypage(); - - const artworks = userMypageData.myCollection.artworks; - - return ( - -

구매 작품

- - - 작품 - - {artworks?.map((artwork) => ( - handleArtworkClick(artwork.id)} - /> - ))} - - -
- ); -}; - -export default PurchasedWorks; diff --git a/src/pages/artBuyerPage/hooks/useGetBuyerMypage.ts b/src/pages/artBuyerPage/hooks/useGetBuyerMypage.ts deleted file mode 100644 index 878f529..0000000 --- a/src/pages/artBuyerPage/hooks/useGetBuyerMypage.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { getMypageQueryKey } from '@/constants/queryKeys'; -import { TBuyerMypage } from '@/apis/mypage-buyer/myPage/type'; -import { getBuyerMypage } from '@/apis/mypage-buyer/myPage/myPage'; - -/** - * 특정 사용자의 마이페이지 정보를 가져오는 커스텀 훅 - * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. - * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. - * @returns {object} userMypageData, isLoading, error 상태 반환 - * @author 노찬영 - */ - -export const useGetBuyerMypage = () => { - const { - data: userMypageData, - isLoading, - error, - } = useSuspenseQuery({ - queryKey: getMypageQueryKey('buyer'), - queryFn: () => getBuyerMypage(), - staleTime: 1000 * 60 * 30, // 30분 - gcTime: 1000 * 60 * 60, // 1시간 - }); - - if (error) { - const axiosError = error as AxiosError<{ message: string }>; - const errorMessage = - axiosError.response?.data?.message || - '마이페이지 정보를 불러오는데 실패했습니다.'; - toast.error(errorMessage); - } - - return { userMypageData, isLoading, error }; -}; diff --git a/src/pages/artBuyerPage/hooks/useGetPurchasedArtworks.ts b/src/pages/artBuyerPage/hooks/useGetPurchasedArtworks.ts deleted file mode 100644 index ba4efbd..0000000 --- a/src/pages/artBuyerPage/hooks/useGetPurchasedArtworks.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { getPurchasedArtworks } from '@/apis/mypage-buyer/artWork'; -import { getPurchasedArtworksQuery } from '@/constants/queryKeys'; -import { TPurchasedArtwork } from '@/apis/mypage-buyer/type'; - -/** - * 구매한 작품 리스트를 가져오는 커스텀 훅 - * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. - * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. - * @returns {object} purchasedArtworksData, isLoading, error 상태 반환 - * @author 노찬영 - **/ -export const useGetPurchasedArtworks = () => { - const { - data: purchasedArtworksData, - isLoading, - error, - } = useSuspenseQuery({ - queryKey: getPurchasedArtworksQuery().queryKey, - queryFn: getPurchasedArtworks, - staleTime: 1000 * 60 * 60, // 1시간 - gcTime: 1000 * 60 * 30, // 30분 - }); - - if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '구매한 작품 리스트를 불러오는데 실패했습니다.'; - toast.error(errorMessage); - } - - return { purchasedArtworksData, isLoading, error }; -}; \ No newline at end of file diff --git a/src/pages/artBuyerPage/hooks/useKakaoPay.ts b/src/pages/artBuyerPage/hooks/useKakaoPay.ts deleted file mode 100644 index b1f2695..0000000 --- a/src/pages/artBuyerPage/hooks/useKakaoPay.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; -import { requestKakaoPay } from '@/apis/kakaoPay/paymentPreparation'; -import { approveKakaoPay } from '@/apis/kakaoPay/paymentAuthorization'; - -/** - * 카카오페이 결제 Hook - * @returns 카카오페이 결제 관련 함수 및 상태값 - * @author 노찬영 - */ - -export const useKakaoPay = () => { - // 결제 준비 (결제 URL 요청) - const initiatePaymentMutation = useMutation({ - mutationKey: ['requestKakaoPay'], - mutationFn: requestKakaoPay, - onSuccess: (result) => { - console.log('결제 준비 성공:', result); - window.location.href = result.next_redirect_pc_url; // PC 결제창으로 이동 - }, - onError: (error) => { - console.error('결제 준비 실패:', error); - }, - }); - - // 결제 승인 (pgToken을 받아 결제 승인 요청) - const confirmPaymentMutation = useMutation({ - mutationKey: ['approveKakaoPay'], - mutationFn: ({ - paymentId, - pgToken, - }: { - paymentId: number; - pgToken: string; - }) => approveKakaoPay(paymentId, pgToken), - onSuccess: (approvedPaymentId) => { - console.log(`결제 승인 완료! Payment ID: ${approvedPaymentId}`); - alert( - `결제가 성공적으로 완료되었습니다. Payment ID: ${approvedPaymentId}` - ); - }, - onError: (error) => { - console.error('결제 승인 실패:', error); - alert('결제 승인 중 오류가 발생했습니다.'); - }, - }); - - return { - initiatePayment: initiatePaymentMutation.mutate, - confirmPayment: confirmPaymentMutation.mutate, - }; -}; diff --git a/src/pages/artBuyerPage/hooks/useUpdateUserInfo.ts b/src/pages/artBuyerPage/hooks/useUpdateUserInfo.ts deleted file mode 100644 index cd99547..0000000 --- a/src/pages/artBuyerPage/hooks/useUpdateUserInfo.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { updateUserInfo } from '@/apis/mypage-buyer/artBuyer'; -import { getUpdateUserInfoQueryKey } from '@/constants/queryKeys'; -import { getQueryClient } from '@/contexts/query/getQueryClient'; -import { TGetResponse } from '@/apis/type'; - -const queryClient = getQueryClient(); - -interface UpdateUserInfoParams { - nickname?: string; - address?: string; - birth?: string; -} - -/** - * 작품 구매자 계정 정보 수정을 위한 React Query 훅 - * @returns mutate 함수와 상태를 반환하여 사용자 정보 수정 요청 가능 - * @example - * const { mutate: updateUser } = useUpdateUserInfo(); - * updateUser({ nickname: '새 닉네임' }); - * @author 노찬영 - **/ -export const useUpdateUserInfo = () => { - const { - mutate: updateUser, - isPending, - error, - } = useMutation< - TGetResponse, - AxiosError<{ message: string }>, - UpdateUserInfoParams - >({ - mutationKey: getUpdateUserInfoQueryKey(), - mutationFn: updateUserInfo, - onSuccess: () => { - toast.success('계정 정보가 성공적으로 업데이트되었습니다.'); - queryClient.invalidateQueries({ queryKey: getUpdateUserInfoQueryKey() }); - }, - onError: (error) => { - const errorMessage = - error.response?.data?.message || '계정 정보 수정에 실패했습니다.'; - toast.error(errorMessage); - }, - }); - - return { updateUser, isPending, error }; -}; diff --git a/src/pages/authorPage/AuthorPage.style.ts b/src/pages/authorPage/AuthorPage.style.ts deleted file mode 100644 index 9f2512a..0000000 --- a/src/pages/authorPage/AuthorPage.style.ts +++ /dev/null @@ -1,19 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const MyPageWrapper = styled.div` - display: flex; - flex-direction: column; - width: auto; - margin: 0 245px auto; - align-items: flex-end; - margin-bottom: 200px; - background-color: ${theme.colors.white}; -`; - -export const MenuWrapper = styled.div` - display: flex; - justify-content: flex-end; - background-color: ${theme.colors.white}; - padding: 80px 220px 100px 0; -`; diff --git a/src/pages/authorPage/AuthorPage.tsx b/src/pages/authorPage/AuthorPage.tsx deleted file mode 100644 index 9eb07b9..0000000 --- a/src/pages/authorPage/AuthorPage.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useState } from 'react'; -import { MenuChooser } from './components/menuChooser'; -import { PageLayout } from '@/components/common/PageLayout'; - -import MenuMyPage from './components/menuMyPage'; -import ManagingProfiles from './components/managingProfiles'; -import AccountSettings from './components/accountSettings'; -import ManagementWorks from './components/managementWorks'; - -import { MenuWrapper, MyPageWrapper } from './AuthorPage.style'; - -export default function AuthorPage() { - const [selectedMenu, setSelectedMenu] = useState< - '마이페이지' | '프로필 관리' | '계정설정' | '작품/전시 관리' - >('마이페이지'); - - // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 - const renderSelectedMenu = () => { - switch (selectedMenu) { - case '마이페이지': - return ; - case '프로필 관리': - return ; - case '계정설정': - return ; - case '작품/전시 관리': - return ; - default: - return null; - } - }; - - return ( - - {/* 메뉴 선택 컴포넌트 */} - - - - - {/* 선택된 메뉴에 따라 컴포넌트 변경 */} - {renderSelectedMenu()} - - ); -} diff --git a/src/pages/authorPage/components/accountSettings/account/index.style.ts b/src/pages/authorPage/components/accountSettings/account/index.style.ts deleted file mode 100644 index 73b796f..0000000 --- a/src/pages/authorPage/components/accountSettings/account/index.style.ts +++ /dev/null @@ -1,77 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - width: 1080px; - height: 56px; - background-color: ${theme.colors.white}; -`; - -export const SectionTitle = styled.h1` - margin: 0; - ${theme.typography['24']} - font-weight: 600; - padding-bottom: 20px; - border-bottom: 2px solid ${theme.colors.black}; -`; - -export const InputContainer = styled.section` - display: flex; - width: 800px; - height: auto; - flex-direction: column; - align-items: flex-start; - gap: 20px; - padding: 64px 0 48px 140px; - - label { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - } -`; - -export const InputField = styled.div` - display: flex; - align-items: center; - gap: 17px; - - input { - width: 629px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } -`; - -export const ButtonContainer = styled.div` - display: flex; - width: 100%; - padding: 48px 0 104px; - justify-content: center; - - gap: 8px; - - border-top: 1px solid ${theme.colors.lineLightColor}; - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` - width: 106px; - padding: 10px 20px; - font-size: 14px; - font-weight: bold; - cursor: pointer; - border: ${({ variant }) => - variant === 'white' ? `1px solid ${theme.colors.black}` : 'none'}; - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.white}; - color: ${({ variant }) => - variant === 'black' ? theme.colors.white : theme.colors.black}; - - &:hover { - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.lightGray}; - } -`; diff --git a/src/pages/authorPage/components/accountSettings/account/index.tsx b/src/pages/authorPage/components/accountSettings/account/index.tsx deleted file mode 100644 index 8284851..0000000 --- a/src/pages/authorPage/components/accountSettings/account/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useState } from 'react'; -import { - ButtonContainer, - FormContainer, - InputContainer, - InputField, - SectionTitle, - StyledButton, -} from './index.style'; -import { useSaveAuthorBankInfo } from '@/pages/authorPage/hooks/useSaveAuthorBankInfo'; - -export const Account = () => { - const [bankName, setBankName] = useState(''); - const [accountHolder, setAccountHolder] = useState(''); - const [accountNumber, setAccountNumber] = useState(''); - const { mutate: saveBankInfo, isPending } = useSaveAuthorBankInfo(); - - const handleSave = () => { - if (!bankName || !accountHolder || !accountNumber) { - alert('모든 필드를 입력해주세요.'); - return; - } - - saveBankInfo({ - bank_name: bankName, - account_holder: accountHolder, - account_number: accountNumber, - }); - }; - - return ( - - 계좌 관리 - - - - setBankName(e.target.value)} - /> - - - - setAccountHolder(e.target.value)} - /> - - - - setAccountNumber(e.target.value)} - /> - - - - 취소 - - {isPending ? '저장 중...' : '저장하기'} - - - - ); -}; diff --git a/src/pages/authorPage/components/accountSettings/basicInfo/index.style.ts b/src/pages/authorPage/components/accountSettings/basicInfo/index.style.ts deleted file mode 100644 index dfef5b6..0000000 --- a/src/pages/authorPage/components/accountSettings/basicInfo/index.style.ts +++ /dev/null @@ -1,201 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - width: 1080px; - margin: 0 auto; - background-color: ${theme.colors.white}; - - h1 { - margin: 0; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - padding-bottom: 20px; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const SectionTitle = styled.span` - ${theme.typography['20']} - font-weight: 600; - color: ${theme.colors.black}; -`; - -export const AccountInfo = styled.section` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 38px; - padding: 64px 0 48px 140px; - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const ProfileImageContainer = styled.div` - position: relative; - - width: 100px; - height: 100px; - - svg { - position: absolute; - right: 0; - bottom: 0; - } -`; - -export const ProfileImage = styled.div` - width: 100px; - height: 100px; - border-radius: 50%; - background-color: ${theme.colors.lightGray}; -`; - -export const UserDetails = styled.div` - display: flex; - flex-direction: column; - gap: 32px; - margin-top: calc(50px - 38px); - - div { - display: flex; - width: 257px; - height: 20px; - gap: 17px; - align-items: center; - } - - span { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.black}; - } -`; - -export const InputContainer = styled.section` - display: flex; - width: 800px; - height: auto; - flex-direction: column; - align-items: flex-start; - gap: 20px; - padding: 64px 0 50px 140px; - border-bottom: 1px solid ${theme.colors.lineLightColor}; - - label { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - } -`; - -export const BasicField = styled.div` - display: flex; - align-items: center; - gap: 17px; - - .name { - width: 120px; - margin-bottom: 12px; - - ${theme.typography['14']} - font-weight: 400; - } - - span { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - } -`; - -export const PhotoField = styled.div` - display: inline-flex; - width: 497px; - height: 120px; - align-items: flex-start; - gap: 17px; - - label { - width: 120px; - ${theme.typography['14']} - font-weight: 400; - } -`; - -export const InputField = styled.div` - display: flex; - align-items: center; - gap: 17px; - - input { - width: 629px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } - - .address-container { - display: flex; - flex-direction: column; - gap: 10px; - - .primary-address { - display: flex; - align-items: center; - gap: 10px; - - input { - width: 512px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } - } - - input:last-child { - width: 631px; - padding: 14px 16px; - border: 1px solid ${theme.colors.lightGray}; - ${theme.typography['16']} - font-weight: 400; - } - } -`; - -export const AddressButton = styled.button` - display: flex; - justify-content: center; - align-items: center; - width: 107px; - padding: 14px 16px; - ${theme.typography['16']} - font-weight: 600; - cursor: pointer; - background-color: ${theme.colors.white}; - border: 1px solid ${theme.colors.black}; -`; - -export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` - width: 106px; - padding: 12px 16px; - font-size: 14px; - font-weight: bold; - cursor: pointer; - border: ${({ variant }) => - variant === 'white' ? `1px solid ${theme.colors.lightGray}` : 'none'}; - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.white}; - color: ${({ variant }) => - variant === 'black' ? theme.colors.white : theme.colors.black}; -`; - -export const ButtonContainer = styled.div` - display: flex; - justify-content: center; - gap: 8px; - margin-top: 48px; -`; diff --git a/src/pages/authorPage/components/accountSettings/basicInfo/index.tsx b/src/pages/authorPage/components/accountSettings/basicInfo/index.tsx deleted file mode 100644 index 59aef90..0000000 --- a/src/pages/authorPage/components/accountSettings/basicInfo/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { useState } from 'react'; - -import { - AccountInfo, - AddressButton, - BasicField, - ButtonContainer, - FormContainer, - InputContainer, - InputField, - ProfileImage, - ProfileImageContainer, - SectionTitle, - StyledButton, - UserDetails, -} from './index.style'; - -import IconCamera from '@assets/svg/Icon_Camera.svg'; -import EditButton from '@assets/svg/Icon_Edit.svg?react'; -import { - PhotoFieldContainer, - PhotoUploadBox, - Placeholder, -} from '../../managingProfiles/introduce/index.style'; - -export const BasicInfo = () => { - const [authorPhoto, setAuthorPhoto] = useState(null); - const [introPhoto, setIntroPhoto] = useState(null); - - const handleImageUpload = ( - event: React.ChangeEvent, - setImage: React.Dispatch> - ) => { - const file = event.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = () => { - setImage(reader.result as string); - }; - reader.readAsDataURL(file); - } - }; - - return ( - -

계정 설정

- {/* TODO[찬영] - 로그인 API Response 데이터 연결 */} - - 계정 정보 - - - - - -
- 닉네임 - 홍길동 -
-
- 이메일 - artné@gmail.com -
-
- 타입 - 작가 -
-
-
- - 기본 정보 - -
이름
-
홍길동
-
- - 휴대전화 - 010-1234-1234 - - - - - - - {/* 작가 사진 */} - - - - handleImageUpload(e, setAuthorPhoto)} - /> - {authorPhoto ? ( - 작가 사진 미리보기 - ) : ( - 카메라 아이콘 - )} - - - 작가 사진 최적 사이즈는 000*000입니다 - - - - {/* 소개 사진 */} - - - - handleImageUpload(e, setIntroPhoto)} - /> - {introPhoto ? ( - 소개 사진 미리보기 - ) : ( - 카메라 아이콘 - )} - - - 소개 사진 최적 사이즈는 000*000입니다 - - - - - - - - - -
-
- - {/* TODO[찬영] - 주소찾기 버튼 클릭시 검색 페이지로 이동 */} - 주소찾기 -
- -
-
-
- - 취소 - 저장하기 - -
- ); -}; diff --git a/src/pages/authorPage/components/accountSettings/index.style.ts b/src/pages/authorPage/components/accountSettings/index.style.ts deleted file mode 100644 index d1b1dc6..0000000 --- a/src/pages/authorPage/components/accountSettings/index.style.ts +++ /dev/null @@ -1,12 +0,0 @@ -import styled from '@emotion/styled'; - -export const TabBoxWrapper = styled.div` - display: flex; - margin-right: 100px; -`; - -export const AccountSettingWrapper = styled.div` - display: flex; - justify-content: center; - width: 100%; -`; diff --git a/src/pages/authorPage/components/accountSettings/index.tsx b/src/pages/authorPage/components/accountSettings/index.tsx deleted file mode 100644 index a985dcb..0000000 --- a/src/pages/authorPage/components/accountSettings/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useState } from 'react'; -import { BasicInfo } from './basicInfo'; -import { Account } from './account'; -import { Withdraw } from './withdraw'; - -import { MyPageSideBar } from '@/components/common/MyPageSideBar'; - -import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; - -export const AccountSettings = () => { - const [selectedTab, setSelectedTab] = useState< - '기본정보 관리' | '계좌 관리' | '회원 탈퇴' - >('기본정보 관리'); - - // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 - const renderSelectedTab = () => { - switch (selectedTab) { - case '기본정보 관리': - return ; - case '계좌 관리': - return ; - case '회원 탈퇴': - return ; - default: - return null; - } - }; - - return ( - - {/* 메뉴 선택 컴포넌트 */} - - - - - {/* 선택된 메뉴에 따라 컴포넌트 변경 */} - {renderSelectedTab()} - - ); -}; - -export default AccountSettings; diff --git a/src/pages/authorPage/components/accountSettings/withdraw/index.style.ts b/src/pages/authorPage/components/accountSettings/withdraw/index.style.ts deleted file mode 100644 index e68f23b..0000000 --- a/src/pages/authorPage/components/accountSettings/withdraw/index.style.ts +++ /dev/null @@ -1,48 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - width: 1080px; - height: 56px; - background-color: ${theme.colors.white}; -`; - -export const SectionTitle = styled.h1` - margin: 0; - ${theme.typography['24']} - font-weight: 600; - padding-bottom: 20px; - border-bottom: 2px solid ${theme.colors.black}; -`; - -export const ButtonContainer = styled.div` - display: flex; - width: 100%; - padding: 48px 0 104px; - justify-content: center; - margin-top: 315px; - - gap: 8px; - - border-top: 1px solid ${theme.colors.lineLightColor}; - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` - width: 106px; - padding: 10px 20px; - font-size: 14px; - font-weight: bold; - cursor: pointer; - border: ${({ variant }) => - variant === 'white' ? `1px solid ${theme.colors.black}` : 'none'}; - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.white}; - color: ${({ variant }) => - variant === 'black' ? theme.colors.white : theme.colors.black}; - - &:hover { - background-color: ${({ variant }) => - variant === 'black' ? theme.colors.black : theme.colors.lightGray}; - } -`; diff --git a/src/pages/authorPage/components/accountSettings/withdraw/index.tsx b/src/pages/authorPage/components/accountSettings/withdraw/index.tsx deleted file mode 100644 index a3cf8bd..0000000 --- a/src/pages/authorPage/components/accountSettings/withdraw/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - ButtonContainer, - FormContainer, - SectionTitle, - StyledButton, -} from './index.style'; - -export const Withdraw = () => { - return ( - - 회원 탈퇴 - - - 취소 - 탈퇴하기 - - - ); -}; diff --git a/src/pages/authorPage/components/managementWorks/artWorks/index.style.ts b/src/pages/authorPage/components/managementWorks/artWorks/index.style.ts deleted file mode 100644 index 9726273..0000000 --- a/src/pages/authorPage/components/managementWorks/artWorks/index.style.ts +++ /dev/null @@ -1,60 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const ArtWorksContainer = styled.div` - display: flex; - flex-direction: column; - width: 1080px; - height: auto; - - gap: 32px; - - h1 { - margin: 0; - - padding-bottom: 20px; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const ArtworkContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const ArtworkGrid = styled.div` - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 40px 20px; - padding-bottom: 64px; - border-bottom: 1px solid ${theme.colors.border}; -`; - -export const ButtonContainer = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - margin-top: 8px; -`; - -export const StyledButton = styled.button` - width: 106px; - padding: 12px 16px; - ${theme.typography['14']} - font-weight: 600; - cursor: pointer; - border: 1px solid ${theme.colors.black}; - background-color: ${theme.colors.white}; - color: ${theme.colors.black}; - - svg { - width: 12px; - height: 12px; - margin-right: 2px; - } -`; diff --git a/src/pages/authorPage/components/managementWorks/artWorks/index.tsx b/src/pages/authorPage/components/managementWorks/artWorks/index.tsx deleted file mode 100644 index 32b9bd3..0000000 --- a/src/pages/authorPage/components/managementWorks/artWorks/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import IconPlus from '@assets/svg/Icon_Plus.svg?react'; -import { Artwork } from '@/components/common/ArtWork'; -import { - ArtworkContainer, - ArtworkGrid, - ArtWorksContainer, - ButtonContainer, - StyledButton, -} from './index.style'; - -import { useGetAuthorArtworksExhibitions } from '@/pages/authorPage/hooks/useGetAuthorArtworksExhibitions'; - -const ArtWorks = () => { - const navigate = useNavigate(); - const { data } = useGetAuthorArtworksExhibitions(); - - const artworks = data?.artworks || []; - - return ( - -

작품

- - - {artworks.length > 0 ? ( - - {artworks.map((artwork) => ( - - ))} - - ) : ( -

등록된 작품이 없습니다.

- )} -
- - - navigate('/artwork-register')}> - - 작품 등록 - - -
- ); -}; - -export default ArtWorks; diff --git a/src/pages/authorPage/components/managementWorks/auctioningWorks/index.style.ts b/src/pages/authorPage/components/managementWorks/auctioningWorks/index.style.ts deleted file mode 100644 index 9726273..0000000 --- a/src/pages/authorPage/components/managementWorks/auctioningWorks/index.style.ts +++ /dev/null @@ -1,60 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const ArtWorksContainer = styled.div` - display: flex; - flex-direction: column; - width: 1080px; - height: auto; - - gap: 32px; - - h1 { - margin: 0; - - padding-bottom: 20px; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const ArtworkContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const ArtworkGrid = styled.div` - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 40px 20px; - padding-bottom: 64px; - border-bottom: 1px solid ${theme.colors.border}; -`; - -export const ButtonContainer = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - margin-top: 8px; -`; - -export const StyledButton = styled.button` - width: 106px; - padding: 12px 16px; - ${theme.typography['14']} - font-weight: 600; - cursor: pointer; - border: 1px solid ${theme.colors.black}; - background-color: ${theme.colors.white}; - color: ${theme.colors.black}; - - svg { - width: 12px; - height: 12px; - margin-right: 2px; - } -`; diff --git a/src/pages/authorPage/components/managementWorks/auctioningWorks/index.tsx b/src/pages/authorPage/components/managementWorks/auctioningWorks/index.tsx deleted file mode 100644 index b7ba5e3..0000000 --- a/src/pages/authorPage/components/managementWorks/auctioningWorks/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import IconPlus from '@assets/svg/Icon_Plus.svg?react'; -import { Artwork } from '@/components/common/ArtWork'; -import { - ArtworkContainer, - ArtworkGrid, - ArtWorksContainer, - ButtonContainer, - StyledButton, -} from './index.style'; - -import { useGetAuthorArtworksExhibitions } from '@/pages/authorPage/hooks/useGetAuthorArtworksExhibitions'; - -const AuctioningWorks = () => { - const navigate = useNavigate(); - - const { data } = useGetAuthorArtworksExhibitions(); - - const auction_artworks = data?.auction_artworks || []; - - return ( - -

경매 중인 작품

- - - - {auction_artworks.map((auctionArtwork) => ( - - ))} - - - - - navigate('/auction-register')}> - - 경매 등록 - - -
- ); -}; - -export default AuctioningWorks; diff --git a/src/pages/authorPage/components/managementWorks/exhibitions/index.style.ts b/src/pages/authorPage/components/managementWorks/exhibitions/index.style.ts deleted file mode 100644 index 44e8d40..0000000 --- a/src/pages/authorPage/components/managementWorks/exhibitions/index.style.ts +++ /dev/null @@ -1,60 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const ArtWorksContainer = styled.div` - display: flex; - flex-direction: column; - width: 1080px; - height: auto; - - gap: 32px; - - h1 { - margin: 0; - - padding-bottom: 20px; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const ArtworkContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const ArtworkGrid = styled.div` - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 40px 20px; - padding-bottom: 64px; - border-bottom: 1px solid ${theme.colors.border}; -`; - -export const ButtonContainer = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - margin-top: 8px; -`; - -export const StyledButton = styled.button` - width: 106px; - padding: 12px 16px; - ${theme.typography['14']} - font-weight: 600; - cursor: pointer; - border: 1px solid ${theme.colors.black}; - background-color: ${theme.colors.white}; - color: ${theme.colors.black}; - - svg { - width: 12px; - height: 12px; - margin-right: 2px; - } -`; diff --git a/src/pages/authorPage/components/managementWorks/exhibitions/index.tsx b/src/pages/authorPage/components/managementWorks/exhibitions/index.tsx deleted file mode 100644 index d250f61..0000000 --- a/src/pages/authorPage/components/managementWorks/exhibitions/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import IconPlus from '@assets/svg/Icon_Plus.svg?react'; -import { Artwork } from '@/components/common/ArtWork'; - -import { - ArtworkContainer, - ArtworkGrid, - ArtWorksContainer, - ButtonContainer, - StyledButton, -} from './index.style'; - -import { useGetAuthorArtworksExhibitions } from '@/pages/authorPage/hooks/useGetAuthorArtworksExhibitions'; - -const Exhibitions = () => { - const navigate = useNavigate(); - - const { data } = useGetAuthorArtworksExhibitions(); - - const artworks = data?.artworks || []; - - const exhibitions = data?.exhibitions || []; - - return ( - -

진행 중인 전시

- - - - {exhibitions.map((exhibition) => { - const relatedArtwork = artworks.find( - (artwork) => artwork.id === exhibition.id - ); - return ( - - ); - })} - - - - - navigate('/exhibit-register')}> - - 전시 등록 - - -
- ); -}; - -export default Exhibitions; \ No newline at end of file diff --git a/src/pages/authorPage/components/managementWorks/index.style.ts b/src/pages/authorPage/components/managementWorks/index.style.ts deleted file mode 100644 index d1b1dc6..0000000 --- a/src/pages/authorPage/components/managementWorks/index.style.ts +++ /dev/null @@ -1,12 +0,0 @@ -import styled from '@emotion/styled'; - -export const TabBoxWrapper = styled.div` - display: flex; - margin-right: 100px; -`; - -export const AccountSettingWrapper = styled.div` - display: flex; - justify-content: center; - width: 100%; -`; diff --git a/src/pages/authorPage/components/managementWorks/index.tsx b/src/pages/authorPage/components/managementWorks/index.tsx deleted file mode 100644 index f229b20..0000000 --- a/src/pages/authorPage/components/managementWorks/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useState } from 'react'; -import ArtWorks from './artWorks'; -import AuctioningWorks from './auctioningWorks'; -import Exhibitions from './exhibitions'; - -import { MyPageSideBar } from '@/components/common/MyPageSideBar'; -import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; - -export const AccountSettings = () => { - const [selectedTab, setSelectedTab] = useState< - '작품' | '경매 중인 작품' | '진행 중인 전시' - >('작품'); - - // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 - const renderSelectedTab = () => { - switch (selectedTab) { - case '작품': - return ; - case '경매 중인 작품': - return ; - case '진행 중인 전시': - return ; - default: - return null; - } - }; - - return ( - - {/* 메뉴 선택 컴포넌트 */} - - - - - {/* 선택된 메뉴에 따라 컴포넌트 변경 */} - {renderSelectedTab()} - - ); -}; - -export default AccountSettings; diff --git a/src/pages/authorPage/components/managingProfiles/allInfo/index.style.ts b/src/pages/authorPage/components/managingProfiles/allInfo/index.style.ts deleted file mode 100644 index eec89d1..0000000 --- a/src/pages/authorPage/components/managingProfiles/allInfo/index.style.ts +++ /dev/null @@ -1,134 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - display: flex; - flex-direction: column; - width: 1080px; - height: auto; - - gap: 32px; - - h1 { - margin: 0; - - padding-bottom: 20px; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const SectionTitle = styled.span` - ${theme.typography['20']} - font-weight: 600; - color: ${theme.colors.black}; -`; - -export const AccountInfo = styled.section` - display: flex; - align-items: flex-start; - padding: 32px 0 32px 32px; - gap: 24px; - border: 1px solid ${theme.colors.border}; -`; - -export const ProfileImageContainer = styled.div` - display: flex; - align-items: center; - justify-content: center; - width: 108px; - height: 108px; -`; - -export const ProfileImage = styled.img` - width: 100px; - height: 100px; - border-radius: 50%; - background-color: ${theme.colors.lightGray}; -`; - -export const UserDetails = styled.div` - display: flex; - flex-direction: column; - padding: 20px 0; - - gap: 6px; - - div { - display: inline-flex; - width: 103px; - height: 34px; - gap: 6px; - align-items: flex-end; - } - - h2 { - margin: 0; - - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - } - - span { - display: flex; - ${theme.typography['20']} - font-weight: 400; - color: ${theme.colors.black}; - } - - p { - margin: 0; - - ${theme.typography['18']} - font-weight: 400; - color: ${theme.colors.fontGray}; - } -`; - -export const IntroduceContainer = styled.section` - display: inline-flex; - flex-direction: column; - - padding: 28px 120px 28px 32px; - align-items: flex-start; - gap: 18px; - border: 1px solid ${theme.colors.border}; -`; - -export const Content = styled.span` - display: flex; - align-items: center; - width: 928px; - - ${theme.typography['16']} - font-weight: 400; - color: ${theme.colors.font03gray}; -`; - -export const Table = styled.table` - width: 100%; - border-collapse: collapse; -`; - -export const Th = styled.th` - padding: 16px; - background-color: ${theme.colors.priceBox}; - ${theme.typography['16']} - font-weight: 400; - color: ${theme.colors.black}; -`; - -export const Tr = styled.tr` - border-bottom: 1px solid ${theme.colors.border}; -`; - -export const Td = styled.td` - padding: 22px 16px; - text-align: center; - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.fontGray}; -`; diff --git a/src/pages/authorPage/components/managingProfiles/allInfo/index.tsx b/src/pages/authorPage/components/managingProfiles/allInfo/index.tsx deleted file mode 100644 index 469604f..0000000 --- a/src/pages/authorPage/components/managingProfiles/allInfo/index.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { - AccountInfo, - FormContainer, - IntroduceContainer, - Content, - ProfileImage, - ProfileImageContainer, - SectionTitle, - UserDetails, - Table, - Th, - Tr, - Td, -} from './index.style'; - -import { useGetAuthorProfile } from '@/pages/authorPage/hooks/useGetAuthorProfile'; - -export const AllInfo = () => { - // useGetAuthorProfile 훅을 사용하여 'default' 프로필 데이터 가져오기 - const { data: authorData } = useGetAuthorProfile('default'); - - if (!authorData) { - return

데이터를 불러올 수 없습니다.

; - } - - const { - author_name, - author_image_url, - email, - description, - education, - award, - experience, - } = authorData; - - return ( - -

전체보기

- {/* 작가 프로필 조회(전체보기) API 연결 */} - - - - - -
-

{author_name}

- 작가 -
-
-

{email}

-
-
-
- - 자기 소개 - {description} - - - 학력 정보 - - - - - - - - - - - - {education.map((edu, index) => ( - - - - - - - - ))} - -
학교전공상태입학일졸업일
- {edu.school} - {edu.major}{edu.status}{edu.start_date}{edu.end_date}
-
- - 수상 경력 - - - - - - - - - {award.map((award, index) => ( - - - - - ))} - -
기간수상 내용
{award.date} - {award.description} -
-
- - 전시 및 프로젝트 경험 - - - - - - - - - {experience.map((experience, index) => ( - - - - - ))} - -
기간전시 내용
{experience.date} - {experience.description} -
-
-
- ); -}; diff --git a/src/pages/authorPage/components/managingProfiles/index.style.ts b/src/pages/authorPage/components/managingProfiles/index.style.ts deleted file mode 100644 index d1b1dc6..0000000 --- a/src/pages/authorPage/components/managingProfiles/index.style.ts +++ /dev/null @@ -1,12 +0,0 @@ -import styled from '@emotion/styled'; - -export const TabBoxWrapper = styled.div` - display: flex; - margin-right: 100px; -`; - -export const AccountSettingWrapper = styled.div` - display: flex; - justify-content: center; - width: 100%; -`; diff --git a/src/pages/authorPage/components/managingProfiles/index.tsx b/src/pages/authorPage/components/managingProfiles/index.tsx deleted file mode 100644 index 0f7cd15..0000000 --- a/src/pages/authorPage/components/managingProfiles/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useState } from 'react'; -import { AllInfo } from './allInfo'; -import { Introduce } from './introduce'; -import { MyInformation } from './myInformation'; - -import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; -import { MyPageSideBar } from '@/components/common/MyPageSideBar'; - -export const ManagingProfiles = () => { - const [selectedTab, setSelectedTab] = useState< - '전체보기' | '자기 소개' | '자기 정보' - >('전체보기'); - - // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 - const renderSelectedTab = () => { - switch (selectedTab) { - case '전체보기': - return ; - case '자기 소개': - return ; - case '자기 정보': - return ; - default: - return null; - } - }; - - return ( - - {/* 메뉴 선택 컴포넌트 */} - - - - - {/* 선택된 메뉴에 따라 컴포넌트 변경 */} - {renderSelectedTab()} - - ); -}; - -export default ManagingProfiles; diff --git a/src/pages/authorPage/components/managingProfiles/introduce/index.style.ts b/src/pages/authorPage/components/managingProfiles/introduce/index.style.ts deleted file mode 100644 index 9e3ae95..0000000 --- a/src/pages/authorPage/components/managingProfiles/introduce/index.style.ts +++ /dev/null @@ -1,124 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - display: flex; - flex-direction: column; - width: 1080px; - height: auto; - - gap: 32px; - - h1 { - margin: 0; - - padding-bottom: 20px; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const SectionTitleBox = styled.section` - display: flex; - width: 100%; - justify-content: space-between; - align-items: center; -`; - -export const SectionTitle = styled.h2` - display: flex; - text-align: center; - margin: 0; - ${theme.typography['20']} - font-weight: 600; -`; - -export const EditButton = styled.button` - display: flex; - padding: 10px 20px; - justify-content: center; - align-items: center; - gap: 4px; - border-radius: 100px; - - background-color: ${theme.colors.EditButton}; - border: none; - cursor: pointer; - - span { - ${theme.typography['13']} - font-weight: 500; - color: ${theme.colors.black}; - } -`; - -export const IntroduceContainer = styled.section` - display: inline-flex; - flex-direction: column; - - width: 1014px; - padding: 20px 32px; - align-items: flex-start; - gap: 12px; - border: 1px solid ${theme.colors.border}; -`; - -export const Content = styled.span` - display: flex; - align-items: center; - width: 928px; - - ${theme.typography['16']} - font-weight: 400; - color: ${theme.colors.font03gray}; -`; - -export const PhotoFieldContainer = styled.div` - display: flex; - width: 497px; -`; - -export const PhotoUploadBox = styled.div` - position: relative; - width: 120px; - height: 120px; - margin: 0 12px 0 17px; - - display: flex; - align-items: center; - justify-content: center; - - background: rgba(0, 0, 0, 0.2); - cursor: pointer; - overflow: hidden; - - input { - position: absolute; - width: 100%; - height: 100%; - opacity: 0; - cursor: pointer; - } - - .preview { - width: 120px; - height: 120px; - object-fit: cover; - } - - .icon { - width: 60px; - height: 60px; - } -`; - -export const Placeholder = styled.span` - align-self: flex-end; - width: 250px; - - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.fontGray}; -`; diff --git a/src/pages/authorPage/components/managingProfiles/introduce/index.tsx b/src/pages/authorPage/components/managingProfiles/introduce/index.tsx deleted file mode 100644 index 85d94af..0000000 --- a/src/pages/authorPage/components/managingProfiles/introduce/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { - IntroduceContainer, - FormContainer, - SectionTitle, - Content, - SectionTitleBox, - EditButton, -} from './index.style'; - -import EditIcon from '@assets/svg/Icon_Edit.svg?react'; - -import { useGetAuthorProfile } from '@/pages/authorPage/hooks/useGetAuthorProfile'; - -export const Introduce = () => { - // useGetAuthorProfile 훅을 사용하여 'intro' 프로필 데이터 가져오기 - const { data: authorData } = useGetAuthorProfile('intro'); - - if (!authorData) { - return

데이터를 불러올 수 없습니다.

; - } - - const { description, work_style } = authorData; - - return ( - - {/* 작가 프로필 조회(자기소개) API 연결 */} -

자기 소개

- - - - 작가 설명 - - 편집하기 - - - - {description} - - - - - 작업 스타일 - - 편집하기 - - - - {work_style} - -
- ); -}; diff --git a/src/pages/authorPage/components/managingProfiles/myInformation/index.style.ts b/src/pages/authorPage/components/managingProfiles/myInformation/index.style.ts deleted file mode 100644 index ec78ef1..0000000 --- a/src/pages/authorPage/components/managingProfiles/myInformation/index.style.ts +++ /dev/null @@ -1,89 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const FormContainer = styled.div` - display: flex; - flex-direction: column; - width: 1080px; - height: auto; - - gap: 32px; - - h1 { - margin: 0; - - padding-bottom: 20px; - ${theme.typography['24']} - font-weight: 600; - color: ${theme.colors.black}; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const SectionTitleBox = styled.section` - display: flex; - width: 100%; - justify-content: space-between; - align-items: center; -`; - -export const SectionTitle = styled.h2` - margin: 0; - ${theme.typography['20']} - font-weight: 600; -`; - -export const EditButton = styled.button` - display: flex; - padding: 10px 20px; - justify-content: center; - align-items: center; - gap: 4px; - border-radius: 100px; - - background-color: ${theme.colors.EditButton}; - border: none; - cursor: pointer; - - span { - ${theme.typography['13']} - font-weight: 500; - color: ${theme.colors.black}; - } -`; - -export const IntroduceContainer = styled.section` - display: inline-flex; - flex-direction: column; - - width: 1014px; - padding: 28px 32px; - align-items: flex-start; - gap: 24px; - border: 1px solid ${theme.colors.border}; -`; - -export const Table = styled.table` - width: 100%; - border-collapse: collapse; -`; - -export const Th = styled.th` - padding: 16px; - background-color: ${theme.colors.priceBox}; - ${theme.typography['16']} - font-weight: 400; - color: ${theme.colors.black}; -`; - -export const Tr = styled.tr` - border-bottom: 1px solid ${theme.colors.border}; -`; - -export const Td = styled.td` - padding: 22px 16px; - text-align: center; - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.fontGray}; -`; diff --git a/src/pages/authorPage/components/managingProfiles/myInformation/index.tsx b/src/pages/authorPage/components/managingProfiles/myInformation/index.tsx deleted file mode 100644 index 014ad1c..0000000 --- a/src/pages/authorPage/components/managingProfiles/myInformation/index.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { - EditButton, - FormContainer, - IntroduceContainer, - SectionTitle, - SectionTitleBox, - Table, - Td, - Th, - Tr, -} from './index.style'; -import FallbackUI from '@/components/common/FallbackUI'; - -import EditIcon from '@assets/svg/Icon_Edit.svg?react'; - -import { useGetAuthorProfile } from '@/pages/authorPage/hooks/useGetAuthorProfile'; - -export const MyInformation = () => { - // useGetAuthorProfile 훅을 사용하여 'info' 프로필 데이터 가져오기 - const { data: authorData, isLoading } = useGetAuthorProfile('info'); - - if (isLoading) { - return ; - } - - if (!authorData) { - return

데이터를 불러올 수 없습니다.

; - } - - const { education, award, experience } = authorData; - - return ( - -

자기 정보

- - {/* 작가 프로필 조회(자기정보) API 연결 */} - - - 작가 설명 - - 편집하기 - - - - - - - - - - - - - - - {education.map((edu, index) => ( - - - - - - - - ))} - -
학교전공상태입학일졸업일
- {edu.school} - {edu.major}{edu.status}{edu.start_date}{edu.end_date}
-
- - - - 작가 설명 - - 편집하기 - - - - - - - - - - - - {award.map((award, index) => ( - - - - - ))} - -
기간수상 내용
{award.date} - {award.description} -
-
- - - - 작가 설명 - - 편집하기 - - - - - - - - - - - - {experience.map((experience, index) => ( - - - - - ))} - -
기간전시 내용
{experience.date} - {experience.description} -
-
-
- ); -}; diff --git a/src/pages/authorPage/components/menuChooser/index.style.ts b/src/pages/authorPage/components/menuChooser/index.style.ts deleted file mode 100644 index a5f1977..0000000 --- a/src/pages/authorPage/components/menuChooser/index.style.ts +++ /dev/null @@ -1,29 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@styles/theme.ts'; - -export const Wrapper = styled.div` - display: flex; - width: 1080px; - align-items: center; - - background-color: ${theme.colors.white}; - border: 1px solid ${theme.colors.lineLightColor}; -`; - -export const MenuButton = styled.button<{ $isActive: boolean }>` - width: 100%; - padding: 16px; - border: none; - &:hover { - cursor: pointer; - } - - ${theme.typography['16']} - text-align: center; - text-overflow: ellipsis; - background-color: ${({ $isActive, theme }) => - $isActive ? theme.colors.black : 'transparent'}; - color: ${({ $isActive, theme }) => - $isActive ? theme.colors.white : theme.colors.font03gray}; - transition: background-color 0.2s ease, color 0.2s ease; -`; diff --git a/src/pages/authorPage/components/menuChooser/index.tsx b/src/pages/authorPage/components/menuChooser/index.tsx deleted file mode 100644 index 0ffc31a..0000000 --- a/src/pages/authorPage/components/menuChooser/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Wrapper, MenuButton } from './index.style'; - -interface MenuChooserProps { - menus: T[]; - selectedMenu: T; - onSelectMenu: (menu: T) => void; -} - -/** - * 마이페이지 내 메뉴 선택 컴포넌트입니다. 기본 선택 메뉴는 '마이페이지' 입니다. - * `menus`를 props로 받아 다른 페이지에서도 활용 가능하도록 작성하였습니다. - * 컴포넌트 타입을 제네릭으로 설정해 확장성과 타입 안전성을 높였습니다. - * @param {string[]} menus - 메뉴 목록 - * @param {string} selectedMenu - 현재 선택된 메뉴 - * @param {(menu: string) => void} onSelectMenu - 선택된 메뉴를 부모 컴포넌트에 전달하는 콜백 함수 - * @returns {JSX.Element} 메뉴 버튼 리스트를 렌더링하는 컴포넌트 - * - * @example - * @author 노찬영 - */ - -export const MenuChooser = ({ - menus, - selectedMenu, - onSelectMenu, -}: MenuChooserProps) => { - return ( - - {menus.map((menu) => ( - onSelectMenu(menu)} - > - {menu} - - ))} - - ); -}; diff --git a/src/pages/authorPage/components/menuMyPage/Auction/index.style.ts b/src/pages/authorPage/components/menuMyPage/Auction/index.style.ts deleted file mode 100644 index 705694f..0000000 --- a/src/pages/authorPage/components/menuMyPage/Auction/index.style.ts +++ /dev/null @@ -1,61 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const AuctionContainer = styled.div` - display: flex; - width: 1080px; - flex-direction: column; - align-items: flex-start; - color: ${theme.colors.black}; - - h1 { - ${theme.typography['24']} - font-weight: 600; - } -`; - -export const Table = styled.table` - width: 100%; - border-collapse: collapse; - margin-top: 4px; - ${theme.typography['14']} - font-weight: 400; - padding: 22px 16px; - border-top: 2px solid ${theme.colors.black}; -`; - -export const TableHeader = styled.th` - padding: 22px 16px; - border-bottom: 1px solid ${theme.colors.fontGray}; - text-align: center; - ${theme.typography['14']} - font-weight: 600; - - &:nth-of-type(1) { - width: 630px; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4) { - width: 150px; - } -`; - -export const TableRow = styled.tr` - border-bottom: 1px solid ${theme.colors.lineLightColor}; -`; - -export const TableCell = styled.td` - padding: 22px 16px; - - &:nth-of-type(1) { - text-align: left; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4) { - text-align: center; - } -`; diff --git a/src/pages/authorPage/components/menuMyPage/Auction/index.tsx b/src/pages/authorPage/components/menuMyPage/Auction/index.tsx deleted file mode 100644 index d95ba56..0000000 --- a/src/pages/authorPage/components/menuMyPage/Auction/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { - AuctionContainer, - Table, - TableCell, - TableHeader, - TableRow, -} from './index.style'; - -import { useGetAuthorMypage } from '@/pages/authorPage/hooks/useGetAuthorMypage'; - -/** - * @description 작가의 경매 내역을 표시하는 컴포넌트 - * @author 노찬영 - */ - -export const AuthorAuction = () => { - const { userMypageData } = useGetAuthorMypage(); - - // 작가의 경매 내역 가져오기 - const auctions = userMypageData.auctions; - - return ( - -

경매

- - - - 작품정보 - 경매정보 - 금액 - 진행상황 - - - - {auctions.map((auction) => ( - - {`${auction.auction.artwork.title} - ${auction.auction.artwork.author.author_name}`} - - {new Date(auction.bid_date).toLocaleDateString('ko-KR')} - - - {new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: 'KRW', - }).format(auction.bid_price)} - - - {auction.status === 'BID' - ? '경매완료' - : auction.status === 'PARTICIPATE' - ? '입찰중' - : auction.status} - - - ))} - -
-
- ); -}; - -export default AuthorAuction; diff --git a/src/pages/authorPage/components/menuMyPage/artworkCollection/index.style.ts b/src/pages/authorPage/components/menuMyPage/artworkCollection/index.style.ts deleted file mode 100644 index 5b6e6c8..0000000 --- a/src/pages/authorPage/components/menuMyPage/artworkCollection/index.style.ts +++ /dev/null @@ -1,71 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const MyCollectionContainer = styled.div` - width: 1080px; - height: auto; - display: flex; - flex-direction: column; - gap: 32px; - color: ${theme.colors.black}; - - h1 { - margin: 0; - padding: 70px 0 20px; - ${theme.typography['24']} - font-weight: 600; - border-bottom: 2px solid ${theme.colors.black}; - } -`; - -export const ArtworkContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const SectionTitle = styled.h2` - margin: 0; - ${theme.typography['20']} - font-weight: 600; -`; - -export const ArtworkGrid = styled.div` - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 40px 20px; - padding: 18px 0 64px; - border-bottom: 1px solid ${theme.colors.borderBottom}; -`; - -export const ExhibitionContainer = styled.section` - display: flex; - width: 100%; - height: auto; - flex-direction: column; -`; - -export const ExhibitionGrid = styled.div` - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 76px 21px; - margin: 19px 0 80px; -`; - -export const ExhibitionItem = styled.div` - width: 346px; - height: 260px; - - h3 { - color: ${theme.colors.black}; - ${theme.typography['14']} - font-weight: 600; - } -`; - -export const ExhibitionImage = styled.img` - width: 346px; - height: 260px; - object-fit: cover; -`; diff --git a/src/pages/authorPage/components/menuMyPage/artworkCollection/index.tsx b/src/pages/authorPage/components/menuMyPage/artworkCollection/index.tsx deleted file mode 100644 index 86e9926..0000000 --- a/src/pages/authorPage/components/menuMyPage/artworkCollection/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { - MyCollectionContainer, - SectionTitle, - ArtworkGrid, - ExhibitionGrid, - ExhibitionItem, - ExhibitionImage, - ArtworkContainer, - ExhibitionContainer, -} from './index.style'; - -import { Artwork } from '@/components/common/ArtWork'; - -import { useGetAuthorMypage } from '@/pages/authorPage/hooks/useGetAuthorMypage'; - -const ArtworkCollection = () => { - const { userMypageData } = useGetAuthorMypage(); - - const artworks = userMypageData.storage.artworks; - const exhibitions = userMypageData.storage.exhibitions; - - return ( - -

작품 보관함

- - - 작품 - - {artworks.map((artwork) => ( - - ))} - - - - - 전시 - - {exhibitions.map((exhibition) => ( - - -

{exhibition.title}

-
- ))} -
-
-
- ); -}; - -export default ArtworkCollection; diff --git a/src/pages/authorPage/components/menuMyPage/index.style.ts b/src/pages/authorPage/components/menuMyPage/index.style.ts deleted file mode 100644 index c290eaf..0000000 --- a/src/pages/authorPage/components/menuMyPage/index.style.ts +++ /dev/null @@ -1,27 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const MyPageWrapper = styled.div` - display: flex; - justify-content: center; - width: 100%; - background-color: ${theme.colors.white}; -`; - -export const ProfileContainer = styled.div` - display: flex; - flex-direction: column; - width: 250px; - margin-right: 100px; - background-color: ${theme.colors.white}; -`; - -export const MyPageContainer = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; - max-width: 1080px; - gap: 80px; - background-color: ${theme.colors.white}; -`; - diff --git a/src/pages/authorPage/components/menuMyPage/index.tsx b/src/pages/authorPage/components/menuMyPage/index.tsx deleted file mode 100644 index c722c93..0000000 --- a/src/pages/authorPage/components/menuMyPage/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import AuthorAuction from './Auction'; -import ArtworkCollection from './artworkCollection'; -import AuthorProfile from './profile'; -import { - MyPageContainer, - MyPageWrapper, - ProfileContainer, -} from './index.style'; - -export const MenuMyPage = ({ - setSelectedMenu, -}: { - setSelectedMenu: ( - menu: '마이페이지' | '프로필 관리' | '계정설정' | '작품/전시 관리' - ) => void; -}) => { - return ( - - - setSelectedMenu('계정설정')} /> - - - - - - - ); -}; - -export default MenuMyPage; diff --git a/src/pages/authorPage/components/menuMyPage/profile/index.style.ts b/src/pages/authorPage/components/menuMyPage/profile/index.style.ts deleted file mode 100644 index 851ef8d..0000000 --- a/src/pages/authorPage/components/menuMyPage/profile/index.style.ts +++ /dev/null @@ -1,82 +0,0 @@ -import styled from '@emotion/styled'; -import theme from '@/styles/theme'; - -export const ProfileContainer = styled.div` - display: flex; - width: 250px; - height: 260px; - flex-direction: column; - justify-content: center; - align-items: center; - - border: 1px solid ${theme.colors.profileBox}; -`; - -export const ProfileImage = styled.img` - width: 80px; - height: 80px; - border-radius: 50%; - object-fit: fill; -`; - -export const ProfileInfo = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-top: 16px; -`; - -export const Name = styled.span` - ${(theme) => theme.theme.typography['18']} - font-weight: 600; - color: ${theme.colors.black}; -`; - -export const EditButton = styled.button` - display: flex; - width: 180px; - padding: 10px 20px; - justify-content: space-between; - align-items: center; - margin-top: 24px; - border-radius: 100px; - border: none; - cursor: pointer; - - ${theme.typography['13']} - font-weight: 400; - background-color: ${theme.colors.profileButton}; - color: ${theme.colors.font03gray}; -`; - -export const PaymentStatus = styled.div` - display: flex; - width: 202px; - flex-direction: column; - align-items: flex-start; - padding: 28px 4px 0; - gap: 20px; - margin-top: 28px; - border-top: 1px solid ${theme.colors.profileBox}; -`; - -export const PaymentItem = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - width: 202px; - height: 20px; -`; - -export const PaymentLabel = styled.span` - ${theme.typography['14']} - font-weight: 400; - color: ${theme.colors.black}; -`; - -export const PaymentCount = styled.span` - text-align: right; - ${theme.typography['14']} - font-weight: 500; - color: ${theme.colors.black}; -`; diff --git a/src/pages/authorPage/components/menuMyPage/profile/index.tsx b/src/pages/authorPage/components/menuMyPage/profile/index.tsx deleted file mode 100644 index 9b53ba9..0000000 --- a/src/pages/authorPage/components/menuMyPage/profile/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { - ProfileContainer, - ProfileImage, - ProfileInfo, - Name, - EditButton, -} from './index.style'; -import RightArrow from '@assets/svg/right-arrow.svg?react'; -import NoneProfile from '@assets/svg/Icon_Profile.svg'; - -import { useGetAuthorMypage } from '@/pages/authorPage/hooks/useGetAuthorMypage'; - -interface AuthorProfileProps { - onEditProfile: () => void; -} - -/** - * 작가 프로필 컴포넌트입니다. - * 프로필 사진, 작가 이름, 작가 소속, 기본정보 수정 버튼을 표시합니다. - * @param {() => void} onEditProfile - 작가 계정 정보 수정 - * @author 노찬영 - **/ - -export const AuthorProfile = ({ onEditProfile }: AuthorProfileProps) => { - const { userMypageData } = useGetAuthorMypage(); - - const author = userMypageData.author; - - return ( - - - - {author.name} - - 기본정보 수정 - - - - ); -}; - -export default AuthorProfile; diff --git a/src/pages/authorPage/hooks/useGetAuthorArtworksExhibitions.ts b/src/pages/authorPage/hooks/useGetAuthorArtworksExhibitions.ts deleted file mode 100644 index aae8a84..0000000 --- a/src/pages/authorPage/hooks/useGetAuthorArtworksExhibitions.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { getAuthorArtworksExhibitionsQuery } from '@/constants/queryKeys'; -import { getAuthorArtworksExhibitions } from '@/apis/mypage-author/author'; -import { TAuthorArtworksExhibitions } from '@/apis/mypage-author/type'; - -/** - * 작가의 작품, 경매 작품, 전시 정보를 가져오는 커스텀 훅 - * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. - * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. - * - * @returns {object} data, isLoading, error 상태 반환 - * @example const { data } = useGetAuthorArtworksExhibitions(); - * @author 노찬영 - **/ -export const useGetAuthorArtworksExhibitions = () => { - const { data, isLoading, error } = - useSuspenseQuery({ - queryKey: getAuthorArtworksExhibitionsQuery().queryKey, - queryFn: getAuthorArtworksExhibitions, - staleTime: 1000 * 60 * 30, // 30분 - gcTime: 1000 * 60 * 60, // 1시간 - }); - - if (error) { - const axiosError = error as AxiosError<{ message: string }>; - const errorMessage = - axiosError.response?.data?.message || - '작가 작품/전시 정보를 불러오는데 실패했습니다.'; - toast.error(errorMessage); - } - - return { data, isLoading, error }; -}; diff --git a/src/pages/authorPage/hooks/useGetAuthorMypage.ts b/src/pages/authorPage/hooks/useGetAuthorMypage.ts deleted file mode 100644 index b2e158b..0000000 --- a/src/pages/authorPage/hooks/useGetAuthorMypage.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { getMypageQueryKey } from '@/constants/queryKeys'; -import { getAuthorMypage } from '@/apis/mypage-author/myPage/myPage'; -import { TArtistMypage } from '@/apis/mypage-author/myPage/type'; - -/** - * 작가의 마이페이지 정보를 가져오는 커스텀 훅 - * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. - * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. - * @returns {object} userMypageData, isLoading, error 상태 반환 - * @author 노찬영 - */ - -export const useGetAuthorMypage = () => { - const { - data: userMypageData, - isLoading, - error, - } = useSuspenseQuery({ - queryKey: getMypageQueryKey('author'), - queryFn: () => getAuthorMypage(), - staleTime: 1000 * 60 * 30, // 30분 - gcTime: 1000 * 60 * 60, // 1시간 - }); - - if (error) { - const axiosError = error as AxiosError<{ message: string }>; - const errorMessage = - axiosError.response?.data?.message || - '마이페이지 정보를 불러오는데 실패했습니다.'; - toast.error(errorMessage); - } - - return { userMypageData, isLoading, error }; -}; diff --git a/src/pages/authorPage/hooks/useGetAuthorProfile.ts b/src/pages/authorPage/hooks/useGetAuthorProfile.ts deleted file mode 100644 index fc50664..0000000 --- a/src/pages/authorPage/hooks/useGetAuthorProfile.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { getAuthorProfile } from '@/apis/mypage-author/author'; -import { getAuthorProfileQuery } from '@/constants/queryKeys'; -import { TAuthorProfile, AuthorProfileType } from '@/apis/mypage-author/type'; - -/** - * 작가 프로필 조회를 위한 React Query 훅 - * @param type - 조회할 프로필 타입 ('default', 'intro', 'info') - * @returns 작가 프로필 데이터와 쿼리 상태 반환 - * @example - * const { data, isLoading, error } = useGetAuthorProfile('intro'); - * @author 노찬영 - */ -export const useGetAuthorProfile = (type: AuthorProfileType) => { - const { data, isLoading, error } = useSuspenseQuery< - TAuthorProfile, - AxiosError - >({ - queryKey: getAuthorProfileQuery(type).queryKey, - queryFn: async () => { - const response = await getAuthorProfile(type); - - // result가 undefined인 경우 예외 처리 - if (!response) { - throw new Error('작가 프로필 데이터를 불러올 수 없습니다.'); - } - - return response; - }, - staleTime: 1000 * 60 * 30, // 30분 - gcTime: 1000 * 60 * 60, // 1시간 - retry: 1, // 실패 시 1회 재시도 - }); - - if (error) { - const axiosError = error as AxiosError<{ message: string }>; - const errorMessage = - axiosError.response?.data?.message || - '작가 프로필 정보를 불러오는데 실패했습니다.'; - toast.error(errorMessage); - } - - return { data, isLoading, error }; -}; diff --git a/src/pages/authorPage/hooks/useSaveAuthorBankInfo.ts b/src/pages/authorPage/hooks/useSaveAuthorBankInfo.ts deleted file mode 100644 index f2e8793..0000000 --- a/src/pages/authorPage/hooks/useSaveAuthorBankInfo.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { saveAuthorBankInfo } from '@/apis/mypage-author/account'; - -/** - * 작가 계좌 정보 저장을 위한 커스텀 뮤테이션 훅 - * @returns 뮤테이션 객체와 상태(onSuccess, onError 핸들링 포함) - * @author 노찬영 - */ -export const useSaveAuthorBankInfo = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationKey: ['saveAuthorBankInfo'], - mutationFn: saveAuthorBankInfo, - onSuccess: () => { - // 저장 성공 시, 관련 쿼리 캐시 무효화 - queryClient.invalidateQueries({ queryKey: ['authorProfile'] }); - console.log('계좌 정보가 성공적으로 저장되었습니다.'); - }, - onError: (error) => { - console.error('계좌 정보 저장 중 오류 발생:', error); - }, - }); -}; diff --git a/src/pages/authorPage/hooks/useUpdateAuthorInfo.ts b/src/pages/authorPage/hooks/useUpdateAuthorInfo.ts deleted file mode 100644 index 82a5cae..0000000 --- a/src/pages/authorPage/hooks/useUpdateAuthorInfo.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { updateAuthorInfo } from '@/apis/mypage-author/author'; -import { getUpdateAuthorInfoQueryKey } from '@/constants/queryKeys'; -import { getQueryClient } from '@/contexts/query/getQueryClient'; -import { TUpdateAuthorInfo } from '@/apis/mypage-author/type'; - -/** - * 작가 계정 정보 수정을 위한 React Query 훅 - * @returns mutate 함수와 상태를 반환하여 작가 계정 정보 수정 요청 가능 - * @author 노찬영 - **/ -export const useUpdateAuthorInfo = () => { - const queryClient = getQueryClient(); - - const { - mutate: updateAuthor, - isPending, - error, - } = useMutation< - void, - AxiosError<{ message: string }>, - { authorId: number; authorInfo: TUpdateAuthorInfo } - >({ - mutationKey: getUpdateAuthorInfoQueryKey(), - mutationFn: ({ authorId, authorInfo }) => - updateAuthorInfo(authorId, authorInfo), - onSuccess: () => { - toast.success('계정 정보가 성공적으로 업데이트되었습니다.'); - queryClient.invalidateQueries({ - queryKey: getUpdateAuthorInfoQueryKey(), - }); - }, - onError: (error) => { - const errorMessage = - error.response?.data?.message || '계정 정보 수정에 실패했습니다.'; - toast.error(errorMessage); - }, - }); - - return { updateAuthor, isPending, error }; -}; diff --git a/src/pages/authorPage/hooks/useUpdateAuthorProfile.ts b/src/pages/authorPage/hooks/useUpdateAuthorProfile.ts deleted file mode 100644 index 52b2d55..0000000 --- a/src/pages/authorPage/hooks/useUpdateAuthorProfile.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; -import { updateAuthorProfile } from '@/apis/mypage-author/profile'; -import { TUpdateAuthorProfile } from '@/apis/mypage-author/type'; -import { getUpdateAuthorProfileQueryKey } from '@/constants/queryKeys'; -import { getQueryClient } from '@/contexts/query/getQueryClient'; - -/** - * 작가 프로필 개별 정보를 수정하는 React Query 훅 - * @returns mutate 함수와 상태를 반환하여 작가 정보 수정 요청 가능 - * @author 노찬영 - **/ -export const useUpdateAuthorProfile = () => { - const queryClient = getQueryClient(); - - const { - mutate: updateProfile, - isPending, - error, - } = useMutation, TUpdateAuthorProfile>({ - mutationKey: getUpdateAuthorProfileQueryKey(), - mutationFn: updateAuthorProfile, - onSuccess: () => { - toast.success('작가 프로필 정보가 성공적으로 업데이트되었습니다.'); - queryClient.invalidateQueries({ - queryKey: getUpdateAuthorProfileQueryKey(), - }); - }, - onError: (error) => { - const errorMessage = - error.response?.data?.message || '작가 프로필 수정에 실패했습니다.'; - toast.error(errorMessage); - }, - }); - - return { updateProfile, isPending, error }; -}; diff --git a/src/pages/exhibit-register/components/StepOne/index.style.ts b/src/pages/exhibit-register/components/StepOne/index.style.ts deleted file mode 100644 index b52229e..0000000 --- a/src/pages/exhibit-register/components/StepOne/index.style.ts +++ /dev/null @@ -1,65 +0,0 @@ -import theme from '@/styles/theme'; -import styled from '@emotion/styled'; - -export const Container = styled.div` - display: flex; - flex-direction: column; - - justify-content: center; - - width: 100%; - max-width: 1140px; - gap: 20px; -`; - -export const StepContainer = styled.div` - display: flex; - flex-direction: row; - gap: 20px; -`; - -export const GalleryList = styled.div` - flex: 1; - max-width: 200px; - max-height: 540px; - overflow-y: auto; /* 세로 스크롤 */ - overflow-x: hidden; - border-radius: 8px; - padding: 10px; -`; - -export const SelectedGallery = styled.div` - display: flex; - flex-direction: column; - align-items: center; - cursor: pointer; - transition: transform 0.2s; - - &:hover { - transform: scale(1.05); - } -`; - -export const GalleryImage = styled.img<{ selected: boolean }>` - width: 100%; - height: auto; - border: ${({ selected }) => (selected ? '2px solid black' : 'none')}; -`; - -export const SelectedGalleryDisplay = styled.div` - flex: 1; - display: flex; - justify-content: center; - align-items: flex-start; /* 상단 정렬 */ - border-radius: 8px; - max-width: 837px; - max-height: 540px; - padding: 20px; - background-color: ${theme.colors.gray}; -`; - -export const DisplayImage = styled.img` - max-width: 100%; - max-height: 100%; - border-radius: 8px; -`; diff --git a/src/pages/exhibit-register/components/StepOne/index.tsx b/src/pages/exhibit-register/components/StepOne/index.tsx deleted file mode 100644 index 76a757f..0000000 --- a/src/pages/exhibit-register/components/StepOne/index.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { Text } from '@/styles/text'; -import { - StepContainer, - GalleryList, - SelectedGallery, - GalleryImage, - SelectedGalleryDisplay, - DisplayImage, - Container, -} from './index.style'; - -import exhibitionImage1 from '@/assets/png/exhibit-register/exhibition_image_1.png'; -import exhibitionImage2 from '@/assets/png/exhibit-register/exhibition_image_2.png'; -import exhibitionImage3 from '@/assets/png/exhibit-register/exhibition_image_3.png'; -import exhibitionImage4 from '@/assets/png/exhibit-register/exhibition_image_4.png'; -import exhibitionImage5 from '@/assets/png/exhibit-register/exhibition_image_5.png'; -import exhibitionImage6 from '@/assets/png/exhibit-register/exhibition_image_6.png'; -import exhibitionImage7 from '@/assets/png/exhibit-register/exhibition_image_7.png'; -import exhibitionImage8 from '@/assets/png/exhibit-register/exhibition_image_8.png'; - -interface StepOneProps { - handleBackgroundSelect: (imageUrl: string) => void; // File -> string으로 변경 - selectedBackground: string | null; // File -> string으로 변경 -} -interface Gallery { - id: string; - name: string; - imagePath: string; -} - -export const StepOne = ({ - handleBackgroundSelect, - selectedBackground, -}: StepOneProps) => { - const galleries: Gallery[] = [ - { - id: '1', - name: '갤러리 1', - imagePath: exhibitionImage1, - }, - { - id: '2', - name: '갤러리 2', - imagePath: exhibitionImage2, - }, - { - id: '3', - name: '갤러리 3', - imagePath: exhibitionImage3, - }, - { - id: '4', - name: '갤러리 4', - imagePath: exhibitionImage4, - }, - { - id: '5', - name: '갤러리 5', - imagePath: exhibitionImage5, - }, - { - id: '6', - name: '갤러리 6', - imagePath: exhibitionImage6, - }, - { - id: '7', - name: '갤러리 7', - imagePath: exhibitionImage7, - }, - { - id: '8', - name: '갤러리 8', - imagePath: exhibitionImage8, - }, - ]; - - return ( - - - - {galleries.map((gallery) => ( - handleBackgroundSelect(gallery.imagePath)} - > - - - {gallery.name} - - - ))} - - - {selectedBackground ? ( - - ) : ( - - 배경 이미지를 선택하세요 - - )} - - - - ); -}; diff --git a/src/pages/exhibit-register/components/StepThree/index.style.ts b/src/pages/exhibit-register/components/StepThree/index.style.ts deleted file mode 100644 index 0077dc3..0000000 --- a/src/pages/exhibit-register/components/StepThree/index.style.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; - -export const Container = styled.div` - display: flex; - flex-direction: column; - gap: 24px; -`; - -export const CombinedPreview = styled.div` - position: relative; - width: 800px; - height: 500px; - border: 2px dashed ${({ theme }) => theme.colors.lightGray}; - margin: 24px 0; - overflow: hidden; -`; - -export const NameInputContainer = styled.div` - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 32px; -`; - -export const OverlayImage = styled.img<{ - position: { x: number; y: number }; - maxWidth: string; -}>` - position: absolute; - cursor: move; - left: ${({ position }) => position.x}%; - top: ${({ position }) => position.y}%; - transform: translate(-50%, -50%); - max-width: ${({ maxWidth }) => maxWidth}; - border: 2px solid ${({ theme }) => theme.colors.white}; - - transition: all 0.3s ease; - - &:hover { - border-color: ${({ theme }) => theme.colors.primary}; - } -`; - -export const NameInput = styled.input` - -`; -// 기존 컴포넌트와 통합 -export const DisplayImage = styled.img` - object-fit: cover; - width: 100%; - height: 100%; -`; - -// 스타일 상수 -export const inputStyle = css` - padding: 12px 16px; - border-radius: 8px; - font-size: 14px; - transition: border-color 0.3s; - - &:focus { - outline: none; - } -`; diff --git a/src/pages/exhibit-register/components/StepThree/index.tsx b/src/pages/exhibit-register/components/StepThree/index.tsx deleted file mode 100644 index 1d04641..0000000 --- a/src/pages/exhibit-register/components/StepThree/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { useState, useRef } from 'react'; -import { Rnd } from 'react-rnd'; - -interface StepThreeProps { - backgroundImage: string; - overlayImage: string; - handleSubmit: (file: File, title: string) => void; -} - -const StepThree = ({ - backgroundImage, - overlayImage, - handleSubmit, -}: StepThreeProps) => { - const [position, setPosition] = useState({ - x: 50, - y: 50, - width: 100, - height: 100, - }); - const [title, setTitle] = useState(''); - const containerRef = useRef(null); - - const getParentPosition = () => { - return ( - containerRef.current?.getBoundingClientRect() || { width: 0, height: 0 } - ); - }; - - const convertToBase64 = (url: string): Promise => { - return new Promise((resolve, reject) => { - const img = new Image(); - img.crossOrigin = 'anonymous'; // 중요! - img.onload = () => { - const canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - ctx?.drawImage(img, 0, 0); - resolve(canvas.toDataURL('image/png')); - }; - img.onerror = reject; - img.src = url; - }); - }; - - const loadImage = async (base64String: string) => { - const img = new Image(); - img.src = base64String; - await img.decode(); - return img; - }; - - const combineAndSendImages = async () => { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - if (!ctx) return; - - try { - const [bgBase64, overlayBase64] = await Promise.all([ - convertToBase64(backgroundImage), - convertToBase64(overlayImage), - ]); - - const [bgImage, changedOverlayImage] = await Promise.all([ - loadImage(bgBase64 as string), - loadImage(overlayBase64 as string), - ]); - - canvas.width = bgImage.width; - canvas.height = bgImage.height; - - // 배경 이미지 그리기 - ctx.drawImage(bgImage, 0, 0); - - // 오버레이 이미지 그리기 - const overlayX = (position.x / 100) * canvas.width; - const overlayY = (position.y / 100) * canvas.height; - const overlayWidth = (position.width / 100) * canvas.width; - const overlayHeight = (position.height / 100) * canvas.height; - - ctx.drawImage( - changedOverlayImage, - overlayX, - overlayY, - overlayWidth, - overlayHeight - ); - - canvas.toBlob((blob) => { - if (blob) { - const file = new File([blob], 'combined-image.png', { - type: 'image/png', - }); - handleSubmit(file, title); - } - }, 'image/png'); - } catch (error) { - console.error('이미지 처리 중 오류 발생:', error); - } - }; - - return ( -
-
- 배경 - { - const parentRect = getParentPosition(); - setPosition((prev) => ({ - ...prev, - x: (d.x / parentRect.width) * 100, - y: (d.y / parentRect.height) * 100, - })); - }} - onResizeStop={(e, direction, ref, delta, position) => { - const parentRect = getParentPosition(); - const rect = ref.getBoundingClientRect(); - setPosition({ - x: (position.x / parentRect.width) * 100, - y: (position.y / parentRect.height) * 100, - width: (rect.width / parentRect.width) * 100, - height: (rect.height / parentRect.height) * 100, - }); - }} - bounds="parent" - enableResizing={{ - top: true, - right: true, - bottom: true, - left: true, - topRight: true, - bottomRight: true, - bottomLeft: true, - topLeft: true, - }} - > - 오버레이 - -
- -
- setTitle(e.target.value)} - placeholder="작품 이름을 입력하세요" - style={{ - padding: '12px', - fontSize: '16px', - border: '1px solid #ddd', - borderRadius: '4px', - }} - /> - -
-
- ); -}; - -export default StepThree; diff --git a/src/pages/exhibit-register/components/StepTwo/index.style.ts b/src/pages/exhibit-register/components/StepTwo/index.style.ts deleted file mode 100644 index 3b39d88..0000000 --- a/src/pages/exhibit-register/components/StepTwo/index.style.ts +++ /dev/null @@ -1,66 +0,0 @@ -import styled from '@emotion/styled'; - -export const Container = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - width: 100%; - max-width: 1140px; - gap: 20px; -`; - -export const StepContainer = styled.div` - display: flex; - flex-direction: row; - gap: 20px; -`; - -export const GalleryList = styled.div` - flex: 1; - max-width: 200px; - max-height: 540px; - overflow-y: auto; /* 세로 스크롤 */ - overflow-x: hidden; - - border-radius: 8px; - padding: 10px; -`; - -export const SelectedGallery = styled.div` - display: flex; - flex-direction: column; - align-items: center; - cursor: pointer; - transition: transform 0.2s; - - &:hover { - transform: scale(1.05); - } -`; -{ - /**selceted 여부에 따라서 색이나 테두리 색이 변하도록 해야함 */ -} -export const GalleryImage = styled.img<{ selected: boolean }>` - width: 100%; - height: auto; - border-radius: 8px; - border: ${({ selected }) => (selected ? '2px solid #000' : 'none')}; -`; - -export const SelectedGalleryDisplay = styled.div` - flex: 1; - display: flex; - justify-content: center; - align-items: flex-start; /* 상단 정렬 */ - border-radius: 8px; - max-width: 837px; - max-height: 540px; - padding: 20px; - background-color: #f9f9f9; -`; - -export const DisplayImage = styled.img` - max-width: 100%; - max-height: 100%; - border-radius: 8px; -`; diff --git a/src/pages/exhibit-register/components/StepTwo/index.tsx b/src/pages/exhibit-register/components/StepTwo/index.tsx deleted file mode 100644 index dee134b..0000000 --- a/src/pages/exhibit-register/components/StepTwo/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Text } from '@/styles/text'; -import { - StepContainer, - GalleryList, - SelectedGallery, - GalleryImage, - SelectedGalleryDisplay, - DisplayImage, - Container, -} from './index.style.ts'; -import { TGetExhibitAvailableArtworksResponse } from '@/apis/exhibit-register/types'; -import { Link } from 'react-router-dom'; - -interface StepTwoProps { - handleOverlaySelect: (url: string) => void; - selectedBackground: string | null; - selectedOverlay: string | null; - availableArtworks: TGetExhibitAvailableArtworksResponse[] | undefined; -} - -/** - * 전시 등록 페이지 2단계 컴포넌트 - * @author 홍규진 - * */ -export const StepTwo = ({ - handleOverlaySelect, - selectedBackground, - selectedOverlay, - availableArtworks, -}: StepTwoProps) => { - const artworks = availableArtworks; - console.log(artworks); - return ( - - - - {artworks && artworks.length > 0 ? ( - artworks.map((artwork) => ( - handleOverlaySelect(artwork.thumbnail_image_url)} - > - - - {artwork.title} - - - )) - ) : ( - - 전시할 수 있는 작품이 없습니다. -
- 작품 등록하기 -
- )} -
- - {selectedBackground ? ( - - ) : ( - - 오버레이 이미지를 선택하세요 - - )} - -
-
- ); -}; diff --git a/src/pages/exhibit-register/hooks/useExhibitRegister.ts b/src/pages/exhibit-register/hooks/useExhibitRegister.ts deleted file mode 100644 index 1830e5f..0000000 --- a/src/pages/exhibit-register/hooks/useExhibitRegister.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { useState } from 'react'; -import { toast } from 'sonner'; -import { instance } from '@/apis/axios'; -import { TGetResponse } from '@/apis/type'; -import { useNavigate } from 'react-router-dom'; -interface ExhibitState { - step: number; - selectedBackground: string | null; // 배경 이미지 - selectedOverlay: string | null; // 오버레이 이미지 - overlayPosition: { x: number; y: number }; - finalImage: File | null; -} - -export const useExhibitRegister = () => { - const navigate = useNavigate(); - const [state, setState] = useState({ - step: 0, - selectedBackground: null, - selectedOverlay: null, - overlayPosition: { x: 50, y: 50 }, - finalImage: null, - }); - - /** - * 배경 이미지 선택 - * @param imageUrl - 선택된 배경 이미지 URL - * @author 홍규진 - */ - const handleBackgroundSelect = (imageUrl: string) => { - setState((prev) => ({ ...prev, selectedBackground: imageUrl })); - }; - - /** - * 오버레이 이미지 선택 - * 오버레이는 특별하게 이미지 파일이 아닌 이미지 URL로 받아오기 때문에 처리가 다릅니다. - * @param imageUrl - 선택된 오버레이 이미지 URL - * @author 홍규진 - */ - const handleOverlaySelect = async (imageUrl: string) => { - try { - setState((prev) => ({ ...prev, selectedOverlay: imageUrl })); - } catch (error) { - console.error('오버레이 이미지 로드 실패:', error); - } - }; - const setStep = (step: number) => { - setState((prev) => ({ ...prev, step })); - }; - - /** - * 전시 등록 제출 - * @param combinedImageFile - 합성된 전시 이미지 - * @param title - 전시 제목 - * @author 홍규진 - */ - const handleSubmit = async (combinedImageFile: File, title: string) => { - if (!title.trim()) { - toast.error('전시 제목을 입력해주세요.'); - return; - } - - const formData = new FormData(); - formData.append('exhibition_image', combinedImageFile); - formData.append('title', title); - - const response = await instance.post>( - '/api/exhibition', - formData - ); - if (response.data.isSuccess) { - toast.success('전시 등록이 완료되었습니다.'); - navigate('/'); - } else { - toast.error('전시 등록에 실패했습니다.'); - } - }; - - return { - ...state, - setStep, - handleBackgroundSelect, - handleOverlaySelect, - handleSubmit, - }; -}; diff --git a/src/pages/exhibit-register/hooks/useGetExhibitAvailableArtwork.ts b/src/pages/exhibit-register/hooks/useGetExhibitAvailableArtwork.ts deleted file mode 100644 index 4c0f2ee..0000000 --- a/src/pages/exhibit-register/hooks/useGetExhibitAvailableArtwork.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getExhibitAvailableArtworkQuery } from '@/constants/queryKeys'; -import { useQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -/** - * 전시 등록시 등록 가능 작품 조회 훅 - * @author 홍규진 - * */ -export const useGetExhibitAvailableArtwork = () => { - const { data, error } = useQuery({ - queryKey: getExhibitAvailableArtworkQuery().queryKey, - queryFn: getExhibitAvailableArtworkQuery().queryFn, - staleTime: 1000 * 60 * 60 * 2, // 2시간 - gcTime: 1000 * 60 * 60 * 1.5, // 1.5시간 - }); - if (error) { - toast.error(error.message); - } - return { - data, - }; -}; diff --git a/src/pages/exhibit-register/hooks/useGetExhibitBackground.ts b/src/pages/exhibit-register/hooks/useGetExhibitBackground.ts deleted file mode 100644 index a3e7941..0000000 --- a/src/pages/exhibit-register/hooks/useGetExhibitBackground.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getExhibitBackgroundImagesQuery } from '@/constants/queryKeys'; -import { useQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -/** - * 전시 등록시 최초 배경 이미지 조회 훅 - * @author 홍규진 - * */ -export const useGetExhibitBackgroundImages = () => { - const { data, error } = useQuery({ - queryKey: getExhibitBackgroundImagesQuery().queryKey, - queryFn: getExhibitBackgroundImagesQuery().queryFn, - staleTime: 1000 * 60 * 60 * 2, // 2시간 - gcTime: 1000 * 60 * 60 * 1.5, // 1.5시간 - }); - if (error) { - toast.error(error.message); - } - return { - data, - }; -}; diff --git a/src/pages/exhibit-register/index.style.ts b/src/pages/exhibit-register/index.style.ts deleted file mode 100644 index f42407b..0000000 --- a/src/pages/exhibit-register/index.style.ts +++ /dev/null @@ -1,50 +0,0 @@ -import styled from '@emotion/styled'; - -export const Container = styled.div` - display: flex; - flex-direction: column; - align-items: center; - max-width: 1200px; - margin: 0 auto; - padding: 48px 24px; -`; - -export const GallerySelection = styled.div` - display: flex; - flex-direction: column; - gap: 20px; -`; - -export const SelectedGallery = styled.div` - display: flex; - flex-direction: column; - align-items: center; - cursor: pointer; -`; - -export const GalleryImage = styled.img` - width: 100%; - height: auto; - border-radius: 8px; -`; - -export const ButtonContainer = styled.div` - display: flex; - justify-content: flex-end; - gap: 16px; - margin-top: 20px; -`; - -export const Button = styled.button` - padding: 12px 20px; - background-color: ${({ theme }) => theme.colors.black}; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - transition: background-color 0.2s; - - &:hover { - background-color: ${({ theme }) => theme.colors.primary}; - } -`; diff --git a/src/pages/exhibit-register/index.tsx b/src/pages/exhibit-register/index.tsx deleted file mode 100644 index 7347110..0000000 --- a/src/pages/exhibit-register/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { PageLayout } from '@/components/common/PageLayout'; -import { ButtonContainer, Container } from './index.style.ts'; -import { StepOne } from './components/StepOne'; -import { StepTwo } from './components/StepTwo'; -import { useExhibitRegister } from './hooks/useExhibitRegister'; -import { CommonButton } from '@/components/common/CommonButton/index.tsx'; -import { useGetExhibitAvailableArtwork } from './hooks/useGetExhibitAvailableArtwork.ts'; -import StepThree from './components/StepThree/index.tsx'; - -// 최상위 컴포넌트 통합 -export const ExhibitRegister = () => { - const { - step, - setStep, - selectedBackground, - selectedOverlay, - handleBackgroundSelect, - handleOverlaySelect, - handleSubmit, - } = useExhibitRegister(); - const { data: availableArtworks } = useGetExhibitAvailableArtwork(); - - return ( - - - {step === 0 && ( - - )} - {step === 1 && ( - - )} - {step === 2 && ( - - )} - - - - setStep(step - 1)} - disabled={step === 0} - text="이전 단계" - /> - {step === 2 ? null : ( - setStep(step + 1)} - disabled={ - (step === 0 && !selectedBackground) || - (step === 1 && !selectedOverlay) - } - text="다음 단계" - /> - )} - - - ); -}; diff --git a/src/pages/exhibition-detail/GalleryBox/index.style.ts b/src/pages/exhibition-detail/GalleryBox/index.style.ts deleted file mode 100644 index ff21b26..0000000 --- a/src/pages/exhibition-detail/GalleryBox/index.style.ts +++ /dev/null @@ -1,16 +0,0 @@ -import styled from '@emotion/styled'; - -export const ArtworkItem = styled.div` - display: flex; - flex-direction: column; -`; - -export const ArtworkImage = styled.img` - width: 346px; - height: 230px; -`; - -export const Title = styled.p` - margin-top: 20px; - font-weight: bold; -`; diff --git a/src/pages/exhibition-detail/GalleryBox/index.tsx b/src/pages/exhibition-detail/GalleryBox/index.tsx deleted file mode 100644 index 1ae8e13..0000000 --- a/src/pages/exhibition-detail/GalleryBox/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { ArtworkImage, Title, ArtworkItem } from './index.style'; - -interface GalleryBoxProps { - imageUrl: string; - title: string; - onClick: () => void; -} - -export const GalleryBox = ({ imageUrl, title, onClick }: GalleryBoxProps) => { - return ( - - - {title} - - ); -}; From ca065dc60a405c8c392d14ca4b1abcbaf8d48ea2 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:03:04 +0900 Subject: [PATCH 07/11] =?UTF-8?q?Feat:=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A4=80=EC=88=98=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80=20(page=20=EB=B6=80=EB=B6=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/GalleryBox/index.style.ts | 16 ++ .../components/GalleryBox/index.tsx | 16 ++ .../hooks/useGetExhibitionDetail.ts | 11 +- src/pages/exhibition-detail/index.tsx | 4 +- .../components/StepOne/index.style.ts | 65 ++++++ .../components/StepOne/index.tsx | 110 +++++++++ .../components/StepThree/index.style.ts | 65 ++++++ .../components/StepThree/index.tsx | 210 ++++++++++++++++++ .../components/StepTwo/index.style.ts | 66 ++++++ .../components/StepTwo/index.tsx | 73 ++++++ .../hooks/useExhibitionRegister.ts | 76 +++++++ .../hooks/useGetExhibitAvailableArtwork.ts | 21 ++ .../hooks/useGetExhibitBackground.ts | 21 ++ .../hooks/usePostExhibitionRegister.ts | 36 +++ src/pages/exhibition-register/index.style.ts | 50 +++++ src/pages/exhibition-register/index.tsx | 75 +++++++ .../accountSettings/account/index.style.ts | 77 +++++++ .../accountSettings/account/index.tsx | 69 ++++++ .../accountSettings/basicInfo/index.style.ts | 201 +++++++++++++++++ .../accountSettings/basicInfo/index.tsx | 150 +++++++++++++ .../components/accountSettings/index.style.ts | 12 + .../components/accountSettings/index.tsx | 46 ++++ .../accountSettings/withdraw/index.style.ts | 48 ++++ .../accountSettings/withdraw/index.tsx | 19 ++ .../managementWorks/artWorks/index.style.ts | 60 +++++ .../managementWorks/artWorks/index.tsx | 51 +++++ .../auctioningWorks/index.style.ts | 60 +++++ .../managementWorks/auctioningWorks/index.tsx | 49 ++++ .../exhibitions/index.style.ts | 60 +++++ .../managementWorks/exhibitions/index.tsx | 56 +++++ .../components/managementWorks/index.style.ts | 12 + .../components/managementWorks/index.tsx | 45 ++++ .../managingProfiles/allInfo/index.style.ts | 134 +++++++++++ .../managingProfiles/allInfo/index.tsx | 147 ++++++++++++ .../managingProfiles/index.style.ts | 12 + .../components/managingProfiles/index.tsx | 45 ++++ .../managingProfiles/introduce/index.style.ts | 124 +++++++++++ .../managingProfiles/introduce/index.tsx | 52 +++++ .../myInformation/index.style.ts | 89 ++++++++ .../managingProfiles/myInformation/index.tsx | 146 ++++++++++++ .../components/menuChooser/index.style.ts | 29 +++ .../components/menuChooser/index.tsx | 44 ++++ .../menuMyPage/Auction/index.style.ts | 61 +++++ .../components/menuMyPage/Auction/index.tsx | 62 ++++++ .../artworkCollection/index.style.ts | 71 ++++++ .../menuMyPage/artworkCollection/index.tsx | 61 +++++ .../components/menuMyPage/index.style.ts | 27 +++ .../components/menuMyPage/index.tsx | 30 +++ .../menuMyPage/profile/index.style.ts | 82 +++++++ .../components/menuMyPage/profile/index.tsx | 45 ++++ .../hooks/useGetAuthorArtworksExhibitions.ts | 28 +++ .../hooks/useGetAuthorMypage.ts | 30 +++ .../hooks/useGetAuthorProfile.ts | 27 +++ .../hooks/useSaveAuthorBankInfo.ts | 25 +++ .../hooks/useUpdateAuthorInfo.ts | 40 ++++ .../hooks/useUpdateAuthorProfile.ts | 36 +++ src/pages/mypage-author-page/index.style.ts | 19 ++ src/pages/mypage-author-page/index.tsx | 48 ++++ .../accountSettings/basicInfo/index.style.ts | 187 ++++++++++++++++ .../accountSettings/basicInfo/index.tsx | 116 ++++++++++ .../components/accountSettings/index.style.ts | 12 + .../components/accountSettings/index.tsx | 41 ++++ .../accountSettings/withdraw/index.style.ts | 48 ++++ .../accountSettings/withdraw/index.tsx | 19 ++ .../components/menuChooser/index.style.ts | 29 +++ .../components/menuChooser/index.tsx | 42 ++++ .../menuMyPage/Auction/index.style.ts | 84 +++++++ .../components/menuMyPage/Auction/index.tsx | 74 ++++++ .../menuMyPage/MyCollection/index.style.ts | 71 ++++++ .../menuMyPage/MyCollection/index.tsx | 85 +++++++ .../menuMyPage/Payment/index.style.ts | 86 +++++++ .../components/menuMyPage/Payment/index.tsx | 70 ++++++ .../components/menuMyPage/index.style.ts | 27 +++ .../components/menuMyPage/index.tsx | 28 +++ .../menuMyPage/profile/index.style.ts | 83 +++++++ .../components/menuMyPage/profile/index.tsx | 68 ++++++ .../components/purchasedWorks/index.style.ts | 70 ++++++ .../components/purchasedWorks/index.tsx | 49 ++++ .../hooks/useGetBuyerMypage.ts | 28 +++ .../hooks/useGetPurchasedArtworks.ts | 29 +++ .../mypage-buyer-page/hooks/useKakaoPay.ts | 59 +++++ .../hooks/useUpdateUserInfo.ts | 37 +++ src/pages/mypage-buyer-page/index.style.ts | 19 ++ src/pages/mypage-buyer-page/index.tsx | 45 ++++ 84 files changed, 4940 insertions(+), 10 deletions(-) create mode 100644 src/pages/exhibition-detail/components/GalleryBox/index.style.ts create mode 100644 src/pages/exhibition-detail/components/GalleryBox/index.tsx create mode 100644 src/pages/exhibition-register/components/StepOne/index.style.ts create mode 100644 src/pages/exhibition-register/components/StepOne/index.tsx create mode 100644 src/pages/exhibition-register/components/StepThree/index.style.ts create mode 100644 src/pages/exhibition-register/components/StepThree/index.tsx create mode 100644 src/pages/exhibition-register/components/StepTwo/index.style.ts create mode 100644 src/pages/exhibition-register/components/StepTwo/index.tsx create mode 100644 src/pages/exhibition-register/hooks/useExhibitionRegister.ts create mode 100644 src/pages/exhibition-register/hooks/useGetExhibitAvailableArtwork.ts create mode 100644 src/pages/exhibition-register/hooks/useGetExhibitBackground.ts create mode 100644 src/pages/exhibition-register/hooks/usePostExhibitionRegister.ts create mode 100644 src/pages/exhibition-register/index.style.ts create mode 100644 src/pages/exhibition-register/index.tsx create mode 100644 src/pages/mypage-author-page/components/accountSettings/account/index.style.ts create mode 100644 src/pages/mypage-author-page/components/accountSettings/account/index.tsx create mode 100644 src/pages/mypage-author-page/components/accountSettings/basicInfo/index.style.ts create mode 100644 src/pages/mypage-author-page/components/accountSettings/basicInfo/index.tsx create mode 100644 src/pages/mypage-author-page/components/accountSettings/index.style.ts create mode 100644 src/pages/mypage-author-page/components/accountSettings/index.tsx create mode 100644 src/pages/mypage-author-page/components/accountSettings/withdraw/index.style.ts create mode 100644 src/pages/mypage-author-page/components/accountSettings/withdraw/index.tsx create mode 100644 src/pages/mypage-author-page/components/managementWorks/artWorks/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managementWorks/artWorks/index.tsx create mode 100644 src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.tsx create mode 100644 src/pages/mypage-author-page/components/managementWorks/exhibitions/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managementWorks/exhibitions/index.tsx create mode 100644 src/pages/mypage-author-page/components/managementWorks/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managementWorks/index.tsx create mode 100644 src/pages/mypage-author-page/components/managingProfiles/allInfo/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managingProfiles/allInfo/index.tsx create mode 100644 src/pages/mypage-author-page/components/managingProfiles/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managingProfiles/index.tsx create mode 100644 src/pages/mypage-author-page/components/managingProfiles/introduce/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managingProfiles/introduce/index.tsx create mode 100644 src/pages/mypage-author-page/components/managingProfiles/myInformation/index.style.ts create mode 100644 src/pages/mypage-author-page/components/managingProfiles/myInformation/index.tsx create mode 100644 src/pages/mypage-author-page/components/menuChooser/index.style.ts create mode 100644 src/pages/mypage-author-page/components/menuChooser/index.tsx create mode 100644 src/pages/mypage-author-page/components/menuMyPage/Auction/index.style.ts create mode 100644 src/pages/mypage-author-page/components/menuMyPage/Auction/index.tsx create mode 100644 src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.style.ts create mode 100644 src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.tsx create mode 100644 src/pages/mypage-author-page/components/menuMyPage/index.style.ts create mode 100644 src/pages/mypage-author-page/components/menuMyPage/index.tsx create mode 100644 src/pages/mypage-author-page/components/menuMyPage/profile/index.style.ts create mode 100644 src/pages/mypage-author-page/components/menuMyPage/profile/index.tsx create mode 100644 src/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions.ts create mode 100644 src/pages/mypage-author-page/hooks/useGetAuthorMypage.ts create mode 100644 src/pages/mypage-author-page/hooks/useGetAuthorProfile.ts create mode 100644 src/pages/mypage-author-page/hooks/useSaveAuthorBankInfo.ts create mode 100644 src/pages/mypage-author-page/hooks/useUpdateAuthorInfo.ts create mode 100644 src/pages/mypage-author-page/hooks/useUpdateAuthorProfile.ts create mode 100644 src/pages/mypage-author-page/index.style.ts create mode 100644 src/pages/mypage-author-page/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/accountSettings/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/accountSettings/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/menuChooser/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/menuChooser/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/profile/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/menuMyPage/profile/index.tsx create mode 100644 src/pages/mypage-buyer-page/components/purchasedWorks/index.style.ts create mode 100644 src/pages/mypage-buyer-page/components/purchasedWorks/index.tsx create mode 100644 src/pages/mypage-buyer-page/hooks/useGetBuyerMypage.ts create mode 100644 src/pages/mypage-buyer-page/hooks/useGetPurchasedArtworks.ts create mode 100644 src/pages/mypage-buyer-page/hooks/useKakaoPay.ts create mode 100644 src/pages/mypage-buyer-page/hooks/useUpdateUserInfo.ts create mode 100644 src/pages/mypage-buyer-page/index.style.ts create mode 100644 src/pages/mypage-buyer-page/index.tsx diff --git a/src/pages/exhibition-detail/components/GalleryBox/index.style.ts b/src/pages/exhibition-detail/components/GalleryBox/index.style.ts new file mode 100644 index 0000000..ff21b26 --- /dev/null +++ b/src/pages/exhibition-detail/components/GalleryBox/index.style.ts @@ -0,0 +1,16 @@ +import styled from '@emotion/styled'; + +export const ArtworkItem = styled.div` + display: flex; + flex-direction: column; +`; + +export const ArtworkImage = styled.img` + width: 346px; + height: 230px; +`; + +export const Title = styled.p` + margin-top: 20px; + font-weight: bold; +`; diff --git a/src/pages/exhibition-detail/components/GalleryBox/index.tsx b/src/pages/exhibition-detail/components/GalleryBox/index.tsx new file mode 100644 index 0000000..1ae8e13 --- /dev/null +++ b/src/pages/exhibition-detail/components/GalleryBox/index.tsx @@ -0,0 +1,16 @@ +import { ArtworkImage, Title, ArtworkItem } from './index.style'; + +interface GalleryBoxProps { + imageUrl: string; + title: string; + onClick: () => void; +} + +export const GalleryBox = ({ imageUrl, title, onClick }: GalleryBoxProps) => { + return ( + + + {title} + + ); +}; diff --git a/src/pages/exhibition-detail/hooks/useGetExhibitionDetail.ts b/src/pages/exhibition-detail/hooks/useGetExhibitionDetail.ts index 154a88c..92a022a 100644 --- a/src/pages/exhibition-detail/hooks/useGetExhibitionDetail.ts +++ b/src/pages/exhibition-detail/hooks/useGetExhibitionDetail.ts @@ -1,7 +1,6 @@ import { getExhibitionDetailQeury } from '@/constants/queryKeys'; import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; +import { handleError } from '@/utils/handleError'; import { TGetExhibitionDetailResponse } from '@/apis/exhibition/type'; /** @@ -11,7 +10,7 @@ import { TGetExhibitionDetailResponse } from '@/apis/exhibition/type'; * @param exhibitionId - 전시 id * @returns {object} data, error 상태 반환 * @example const { data } = useGetAuctionLists('title'); - * @author 이하늘 + * @author 이하늘, 홍규진 **/ export const useGetExhibitionDetail = (exhibitionId: number) => { const { data, isLoading, error } = @@ -23,11 +22,7 @@ export const useGetExhibitionDetail = (exhibitionId: number) => { }); if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '전시 상세를를 불러오는데 실패했습니다.'; - toast.error(errorMessage); + handleError(error); } return { data, isLoading, error }; diff --git a/src/pages/exhibition-detail/index.tsx b/src/pages/exhibition-detail/index.tsx index 94bddb9..d3e4250 100644 --- a/src/pages/exhibition-detail/index.tsx +++ b/src/pages/exhibition-detail/index.tsx @@ -12,8 +12,8 @@ import { SubTitle, } from './index.style'; import { useNavigate, useParams } from 'react-router-dom'; -import { GalleryBox } from './GalleryBox'; -import { useGetExhibitionDetail } from './hooks/useGetExhibitionDetail'; +import { GalleryBox } from '@/pages/exhibition-detail/components/GalleryBox'; +import { useGetExhibitionDetail } from '@/pages/exhibition-detail/hooks/useGetExhibitionDetail'; // const exhibitionData = { // id: 1, diff --git a/src/pages/exhibition-register/components/StepOne/index.style.ts b/src/pages/exhibition-register/components/StepOne/index.style.ts new file mode 100644 index 0000000..b52229e --- /dev/null +++ b/src/pages/exhibition-register/components/StepOne/index.style.ts @@ -0,0 +1,65 @@ +import theme from '@/styles/theme'; +import styled from '@emotion/styled'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + + justify-content: center; + + width: 100%; + max-width: 1140px; + gap: 20px; +`; + +export const StepContainer = styled.div` + display: flex; + flex-direction: row; + gap: 20px; +`; + +export const GalleryList = styled.div` + flex: 1; + max-width: 200px; + max-height: 540px; + overflow-y: auto; /* 세로 스크롤 */ + overflow-x: hidden; + border-radius: 8px; + padding: 10px; +`; + +export const SelectedGallery = styled.div` + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + transition: transform 0.2s; + + &:hover { + transform: scale(1.05); + } +`; + +export const GalleryImage = styled.img<{ selected: boolean }>` + width: 100%; + height: auto; + border: ${({ selected }) => (selected ? '2px solid black' : 'none')}; +`; + +export const SelectedGalleryDisplay = styled.div` + flex: 1; + display: flex; + justify-content: center; + align-items: flex-start; /* 상단 정렬 */ + border-radius: 8px; + max-width: 837px; + max-height: 540px; + padding: 20px; + background-color: ${theme.colors.gray}; +`; + +export const DisplayImage = styled.img` + max-width: 100%; + max-height: 100%; + border-radius: 8px; +`; diff --git a/src/pages/exhibition-register/components/StepOne/index.tsx b/src/pages/exhibition-register/components/StepOne/index.tsx new file mode 100644 index 0000000..76a757f --- /dev/null +++ b/src/pages/exhibition-register/components/StepOne/index.tsx @@ -0,0 +1,110 @@ +import { Text } from '@/styles/text'; +import { + StepContainer, + GalleryList, + SelectedGallery, + GalleryImage, + SelectedGalleryDisplay, + DisplayImage, + Container, +} from './index.style'; + +import exhibitionImage1 from '@/assets/png/exhibit-register/exhibition_image_1.png'; +import exhibitionImage2 from '@/assets/png/exhibit-register/exhibition_image_2.png'; +import exhibitionImage3 from '@/assets/png/exhibit-register/exhibition_image_3.png'; +import exhibitionImage4 from '@/assets/png/exhibit-register/exhibition_image_4.png'; +import exhibitionImage5 from '@/assets/png/exhibit-register/exhibition_image_5.png'; +import exhibitionImage6 from '@/assets/png/exhibit-register/exhibition_image_6.png'; +import exhibitionImage7 from '@/assets/png/exhibit-register/exhibition_image_7.png'; +import exhibitionImage8 from '@/assets/png/exhibit-register/exhibition_image_8.png'; + +interface StepOneProps { + handleBackgroundSelect: (imageUrl: string) => void; // File -> string으로 변경 + selectedBackground: string | null; // File -> string으로 변경 +} +interface Gallery { + id: string; + name: string; + imagePath: string; +} + +export const StepOne = ({ + handleBackgroundSelect, + selectedBackground, +}: StepOneProps) => { + const galleries: Gallery[] = [ + { + id: '1', + name: '갤러리 1', + imagePath: exhibitionImage1, + }, + { + id: '2', + name: '갤러리 2', + imagePath: exhibitionImage2, + }, + { + id: '3', + name: '갤러리 3', + imagePath: exhibitionImage3, + }, + { + id: '4', + name: '갤러리 4', + imagePath: exhibitionImage4, + }, + { + id: '5', + name: '갤러리 5', + imagePath: exhibitionImage5, + }, + { + id: '6', + name: '갤러리 6', + imagePath: exhibitionImage6, + }, + { + id: '7', + name: '갤러리 7', + imagePath: exhibitionImage7, + }, + { + id: '8', + name: '갤러리 8', + imagePath: exhibitionImage8, + }, + ]; + + return ( + + + + {galleries.map((gallery) => ( + handleBackgroundSelect(gallery.imagePath)} + > + + + {gallery.name} + + + ))} + + + {selectedBackground ? ( + + ) : ( + + 배경 이미지를 선택하세요 + + )} + + + + ); +}; diff --git a/src/pages/exhibition-register/components/StepThree/index.style.ts b/src/pages/exhibition-register/components/StepThree/index.style.ts new file mode 100644 index 0000000..0077dc3 --- /dev/null +++ b/src/pages/exhibition-register/components/StepThree/index.style.ts @@ -0,0 +1,65 @@ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + gap: 24px; +`; + +export const CombinedPreview = styled.div` + position: relative; + width: 800px; + height: 500px; + border: 2px dashed ${({ theme }) => theme.colors.lightGray}; + margin: 24px 0; + overflow: hidden; +`; + +export const NameInputContainer = styled.div` + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 32px; +`; + +export const OverlayImage = styled.img<{ + position: { x: number; y: number }; + maxWidth: string; +}>` + position: absolute; + cursor: move; + left: ${({ position }) => position.x}%; + top: ${({ position }) => position.y}%; + transform: translate(-50%, -50%); + max-width: ${({ maxWidth }) => maxWidth}; + border: 2px solid ${({ theme }) => theme.colors.white}; + + transition: all 0.3s ease; + + &:hover { + border-color: ${({ theme }) => theme.colors.primary}; + } +`; + +export const NameInput = styled.input` + +`; +// 기존 컴포넌트와 통합 +export const DisplayImage = styled.img` + object-fit: cover; + width: 100%; + height: 100%; +`; + +// 스타일 상수 +export const inputStyle = css` + padding: 12px 16px; + border-radius: 8px; + font-size: 14px; + transition: border-color 0.3s; + + &:focus { + outline: none; + } +`; diff --git a/src/pages/exhibition-register/components/StepThree/index.tsx b/src/pages/exhibition-register/components/StepThree/index.tsx new file mode 100644 index 0000000..1d04641 --- /dev/null +++ b/src/pages/exhibition-register/components/StepThree/index.tsx @@ -0,0 +1,210 @@ +import { useState, useRef } from 'react'; +import { Rnd } from 'react-rnd'; + +interface StepThreeProps { + backgroundImage: string; + overlayImage: string; + handleSubmit: (file: File, title: string) => void; +} + +const StepThree = ({ + backgroundImage, + overlayImage, + handleSubmit, +}: StepThreeProps) => { + const [position, setPosition] = useState({ + x: 50, + y: 50, + width: 100, + height: 100, + }); + const [title, setTitle] = useState(''); + const containerRef = useRef(null); + + const getParentPosition = () => { + return ( + containerRef.current?.getBoundingClientRect() || { width: 0, height: 0 } + ); + }; + + const convertToBase64 = (url: string): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + img.crossOrigin = 'anonymous'; // 중요! + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + ctx?.drawImage(img, 0, 0); + resolve(canvas.toDataURL('image/png')); + }; + img.onerror = reject; + img.src = url; + }); + }; + + const loadImage = async (base64String: string) => { + const img = new Image(); + img.src = base64String; + await img.decode(); + return img; + }; + + const combineAndSendImages = async () => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + try { + const [bgBase64, overlayBase64] = await Promise.all([ + convertToBase64(backgroundImage), + convertToBase64(overlayImage), + ]); + + const [bgImage, changedOverlayImage] = await Promise.all([ + loadImage(bgBase64 as string), + loadImage(overlayBase64 as string), + ]); + + canvas.width = bgImage.width; + canvas.height = bgImage.height; + + // 배경 이미지 그리기 + ctx.drawImage(bgImage, 0, 0); + + // 오버레이 이미지 그리기 + const overlayX = (position.x / 100) * canvas.width; + const overlayY = (position.y / 100) * canvas.height; + const overlayWidth = (position.width / 100) * canvas.width; + const overlayHeight = (position.height / 100) * canvas.height; + + ctx.drawImage( + changedOverlayImage, + overlayX, + overlayY, + overlayWidth, + overlayHeight + ); + + canvas.toBlob((blob) => { + if (blob) { + const file = new File([blob], 'combined-image.png', { + type: 'image/png', + }); + handleSubmit(file, title); + } + }, 'image/png'); + } catch (error) { + console.error('이미지 처리 중 오류 발생:', error); + } + }; + + return ( +
+
+ 배경 + { + const parentRect = getParentPosition(); + setPosition((prev) => ({ + ...prev, + x: (d.x / parentRect.width) * 100, + y: (d.y / parentRect.height) * 100, + })); + }} + onResizeStop={(e, direction, ref, delta, position) => { + const parentRect = getParentPosition(); + const rect = ref.getBoundingClientRect(); + setPosition({ + x: (position.x / parentRect.width) * 100, + y: (position.y / parentRect.height) * 100, + width: (rect.width / parentRect.width) * 100, + height: (rect.height / parentRect.height) * 100, + }); + }} + bounds="parent" + enableResizing={{ + top: true, + right: true, + bottom: true, + left: true, + topRight: true, + bottomRight: true, + bottomLeft: true, + topLeft: true, + }} + > + 오버레이 + +
+ +
+ setTitle(e.target.value)} + placeholder="작품 이름을 입력하세요" + style={{ + padding: '12px', + fontSize: '16px', + border: '1px solid #ddd', + borderRadius: '4px', + }} + /> + +
+
+ ); +}; + +export default StepThree; diff --git a/src/pages/exhibition-register/components/StepTwo/index.style.ts b/src/pages/exhibition-register/components/StepTwo/index.style.ts new file mode 100644 index 0000000..3b39d88 --- /dev/null +++ b/src/pages/exhibition-register/components/StepTwo/index.style.ts @@ -0,0 +1,66 @@ +import styled from '@emotion/styled'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; + max-width: 1140px; + gap: 20px; +`; + +export const StepContainer = styled.div` + display: flex; + flex-direction: row; + gap: 20px; +`; + +export const GalleryList = styled.div` + flex: 1; + max-width: 200px; + max-height: 540px; + overflow-y: auto; /* 세로 스크롤 */ + overflow-x: hidden; + + border-radius: 8px; + padding: 10px; +`; + +export const SelectedGallery = styled.div` + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + transition: transform 0.2s; + + &:hover { + transform: scale(1.05); + } +`; +{ + /**selceted 여부에 따라서 색이나 테두리 색이 변하도록 해야함 */ +} +export const GalleryImage = styled.img<{ selected: boolean }>` + width: 100%; + height: auto; + border-radius: 8px; + border: ${({ selected }) => (selected ? '2px solid #000' : 'none')}; +`; + +export const SelectedGalleryDisplay = styled.div` + flex: 1; + display: flex; + justify-content: center; + align-items: flex-start; /* 상단 정렬 */ + border-radius: 8px; + max-width: 837px; + max-height: 540px; + padding: 20px; + background-color: #f9f9f9; +`; + +export const DisplayImage = styled.img` + max-width: 100%; + max-height: 100%; + border-radius: 8px; +`; diff --git a/src/pages/exhibition-register/components/StepTwo/index.tsx b/src/pages/exhibition-register/components/StepTwo/index.tsx new file mode 100644 index 0000000..261c7cf --- /dev/null +++ b/src/pages/exhibition-register/components/StepTwo/index.tsx @@ -0,0 +1,73 @@ +import { Text } from '@/styles/text'; +import { + StepContainer, + GalleryList, + SelectedGallery, + GalleryImage, + SelectedGalleryDisplay, + DisplayImage, + Container, +} from './index.style.ts'; +import { TGetExhibitAvailableArtworksResponse } from '@/apis/exhibitRegister/types.ts'; +import { Link } from 'react-router-dom'; + +interface StepTwoProps { + handleOverlaySelect: (url: string) => void; + selectedBackground: string | null; + selectedOverlay: string | null; + availableArtworks: TGetExhibitAvailableArtworksResponse[] | undefined; +} + +/** + * 전시 등록 페이지 2단계 컴포넌트 + * @author 홍규진 + * */ +export const StepTwo = ({ + handleOverlaySelect, + selectedBackground, + selectedOverlay, + availableArtworks, +}: StepTwoProps) => { + const artworks = availableArtworks; + console.log(artworks); + return ( + + + + {artworks && artworks.length > 0 ? ( + artworks.map((artwork) => ( + handleOverlaySelect(artwork.thumbnail_image_url)} + > + + + {artwork.title} + + + )) + ) : ( + + 전시할 수 있는 작품이 없습니다. +
+ 작품 등록하기 +
+ )} +
+ + {selectedBackground ? ( + + ) : ( + + 오버레이 이미지를 선택하세요 + + )} + +
+
+ ); +}; diff --git a/src/pages/exhibition-register/hooks/useExhibitionRegister.ts b/src/pages/exhibition-register/hooks/useExhibitionRegister.ts new file mode 100644 index 0000000..c535e16 --- /dev/null +++ b/src/pages/exhibition-register/hooks/useExhibitionRegister.ts @@ -0,0 +1,76 @@ +import { useState } from 'react'; +import { toast } from 'sonner'; +import { usePostExhibitionRegisterMutation } from './usePostExhibitionRegister'; +import { TPostExhibitionRegisterFormData } from '@/apis/exhibitRegister/types'; +interface ExhibitState { + step: number; + selectedBackground: string | null; // 배경 이미지 + selectedOverlay: string | null; // 오버레이 이미지 + overlayPosition: { x: number; y: number }; + finalImage: File | null; +} + +export const useExhibitionRegister = () => { + const { postExhibitionRegister } = usePostExhibitionRegisterMutation(); + const [state, setState] = useState({ + step: 0, + selectedBackground: null, + selectedOverlay: null, + overlayPosition: { x: 50, y: 50 }, + finalImage: null, + }); + + /** + * 배경 이미지 선택 + * @param imageUrl - 선택된 배경 이미지 URL + * @author 홍규진 + */ + const handleBackgroundSelect = (imageUrl: string) => { + setState((prev) => ({ ...prev, selectedBackground: imageUrl })); + }; + + /** + * 오버레이 이미지 선택 + * 오버레이는 특별하게 이미지 파일이 아닌 이미지 URL로 받아오기 때문에 처리가 다릅니다. + * @param imageUrl - 선택된 오버레이 이미지 URL + * @author 홍규진 + */ + const handleOverlaySelect = async (imageUrl: string) => { + try { + setState((prev) => ({ ...prev, selectedOverlay: imageUrl })); + } catch (error) { + console.error('오버레이 이미지 로드 실패:', error); + } + }; + const setStep = (step: number) => { + setState((prev) => ({ ...prev, step })); + }; + + /** + * 전시 등록 제출 + * @param combinedImageFile - 합성된 전시 이미지 + * @param title - 전시 제목 + * @author 홍규진 + */ + const handleSubmit = async (combinedImageFile: File, title: string) => { + if (!title.trim()) { + toast.error('전시 제목을 입력해주세요.'); + return; + } + + const exhibitionData: TPostExhibitionRegisterFormData = { + title, + exhibit_image: combinedImageFile, + }; + + postExhibitionRegister(exhibitionData); + }; + + return { + ...state, + setStep, + handleBackgroundSelect, + handleOverlaySelect, + handleSubmit, + }; +}; diff --git a/src/pages/exhibition-register/hooks/useGetExhibitAvailableArtwork.ts b/src/pages/exhibition-register/hooks/useGetExhibitAvailableArtwork.ts new file mode 100644 index 0000000..374cfb6 --- /dev/null +++ b/src/pages/exhibition-register/hooks/useGetExhibitAvailableArtwork.ts @@ -0,0 +1,21 @@ +import { getExhibitAvailableArtworkQuery } from '@/constants/queryKeys'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import { handleError } from '@/utils/handleError'; +/** + * 전시 등록시 등록 가능 작품 조회 훅 + * @author 홍규진 + * */ +export const useGetExhibitAvailableArtwork = () => { + const { data, error } = useSuspenseQuery({ + queryKey: getExhibitAvailableArtworkQuery().queryKey, + queryFn: getExhibitAvailableArtworkQuery().queryFn, + staleTime: 1000 * 60 * 60 * 2, // 2시간 + gcTime: 1000 * 60 * 60 * 1.5, // 1.5시간 + }); + if (error) { + handleError(error); + } + return { + data, + }; +}; diff --git a/src/pages/exhibition-register/hooks/useGetExhibitBackground.ts b/src/pages/exhibition-register/hooks/useGetExhibitBackground.ts new file mode 100644 index 0000000..4b9981f --- /dev/null +++ b/src/pages/exhibition-register/hooks/useGetExhibitBackground.ts @@ -0,0 +1,21 @@ +import { getExhibitBackgroundImagesQuery } from '@/constants/queryKeys'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import { handleError } from '@/utils/handleError'; +/** + * 전시 등록시 최초 배경 이미지 조회 훅 + * @author 홍규진 + * */ +export const useGetExhibitBackgroundImages = () => { + const { data, error } = useSuspenseQuery({ + queryKey: getExhibitBackgroundImagesQuery().queryKey, + queryFn: getExhibitBackgroundImagesQuery().queryFn, + staleTime: 1000 * 60 * 60 * 2, // 2시간 + gcTime: 1000 * 60 * 60 * 1.5, // 1.5시간 + }); + if (error) { + handleError(error); + } + return { + data, + }; +}; diff --git a/src/pages/exhibition-register/hooks/usePostExhibitionRegister.ts b/src/pages/exhibition-register/hooks/usePostExhibitionRegister.ts new file mode 100644 index 0000000..6cdc5a5 --- /dev/null +++ b/src/pages/exhibition-register/hooks/usePostExhibitionRegister.ts @@ -0,0 +1,36 @@ +/** + * 전시 등록 API 호출 + * @author 홍규진 + */ + +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { TPostExhibitionRegisterFormData } from '@/apis/exhibitRegister/types'; +import { postExhibitionRegisterMutation } from '@/constants/mutationKey'; +import { toast } from 'sonner'; +/** + * 전시 등록 관련 Mutation 훅 + * @author 홍규진 + */ +export const usePostExhibitionRegisterMutation = () => { + const queryClient = useQueryClient(); + const { + mutate: postExhibitionRegister, + isPending, + error, + } = useMutation({ + mutationKey: postExhibitionRegisterMutation().mutationKey, + mutationFn: (formData: TPostExhibitionRegisterFormData) => + postExhibitionRegisterMutation().mutationFn(formData), + onSuccess: () => { + toast.success('전시 등록이 완료되었습니다.'); + queryClient.invalidateQueries({ + queryKey: postExhibitionRegisterMutation().successMutationKey, + }); + }, + onError: (error) => { + toast.error(`전시 등록에 실패했습니다. ${error.message}`); + }, + }); + + return { postExhibitionRegister, isPending, error }; +}; diff --git a/src/pages/exhibition-register/index.style.ts b/src/pages/exhibition-register/index.style.ts new file mode 100644 index 0000000..f42407b --- /dev/null +++ b/src/pages/exhibition-register/index.style.ts @@ -0,0 +1,50 @@ +import styled from '@emotion/styled'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + align-items: center; + max-width: 1200px; + margin: 0 auto; + padding: 48px 24px; +`; + +export const GallerySelection = styled.div` + display: flex; + flex-direction: column; + gap: 20px; +`; + +export const SelectedGallery = styled.div` + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; +`; + +export const GalleryImage = styled.img` + width: 100%; + height: auto; + border-radius: 8px; +`; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: flex-end; + gap: 16px; + margin-top: 20px; +`; + +export const Button = styled.button` + padding: 12px 20px; + background-color: ${({ theme }) => theme.colors.black}; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: ${({ theme }) => theme.colors.primary}; + } +`; diff --git a/src/pages/exhibition-register/index.tsx b/src/pages/exhibition-register/index.tsx new file mode 100644 index 0000000..a4305dd --- /dev/null +++ b/src/pages/exhibition-register/index.tsx @@ -0,0 +1,75 @@ +import { PageLayout } from '@/components/common/PageLayout'; +import { ButtonContainer, Container } from './index.style.ts'; +import { StepOne } from '@/pages/exhibition-register/components/StepOne/index.tsx'; +import { StepTwo } from '@/pages/exhibition-register/components/StepTwo/index.tsx'; +import { useExhibitionRegister } from '@/pages/exhibition-register/hooks/useExhibitionRegister.ts'; +import { CommonButton } from '@/components/common/CommonButton/index.tsx'; +import { useGetExhibitAvailableArtwork } from '@/pages/exhibition-register/hooks/useGetExhibitAvailableArtwork.ts'; +import StepThree from '@/pages/exhibition-register/components/StepThree/index.tsx'; + +// 최상위 컴포넌트 통합 +export const ExhibitionRegister = () => { + const { + step, + setStep, + selectedBackground, + selectedOverlay, + handleBackgroundSelect, + handleOverlaySelect, + handleSubmit, + } = useExhibitionRegister(); + const { data: availableArtworks } = useGetExhibitAvailableArtwork(); + + return ( + + + {step === 0 && ( + + )} + {step === 1 && ( + + )} + {step === 2 && ( + + )} + + + + setStep(step - 1)} + disabled={step === 0} + text="이전 단계" + /> + {step === 2 ? null : ( + setStep(step + 1)} + disabled={ + (step === 0 && !selectedBackground) || + (step === 1 && !selectedOverlay) + } + text="다음 단계" + /> + )} + + + ); +}; diff --git a/src/pages/mypage-author-page/components/accountSettings/account/index.style.ts b/src/pages/mypage-author-page/components/accountSettings/account/index.style.ts new file mode 100644 index 0000000..73b796f --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/account/index.style.ts @@ -0,0 +1,77 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + width: 1080px; + height: 56px; + background-color: ${theme.colors.white}; +`; + +export const SectionTitle = styled.h1` + margin: 0; + ${theme.typography['24']} + font-weight: 600; + padding-bottom: 20px; + border-bottom: 2px solid ${theme.colors.black}; +`; + +export const InputContainer = styled.section` + display: flex; + width: 800px; + height: auto; + flex-direction: column; + align-items: flex-start; + gap: 20px; + padding: 64px 0 48px 140px; + + label { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + } +`; + +export const InputField = styled.div` + display: flex; + align-items: center; + gap: 17px; + + input { + width: 629px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } +`; + +export const ButtonContainer = styled.div` + display: flex; + width: 100%; + padding: 48px 0 104px; + justify-content: center; + + gap: 8px; + + border-top: 1px solid ${theme.colors.lineLightColor}; + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` + width: 106px; + padding: 10px 20px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + border: ${({ variant }) => + variant === 'white' ? `1px solid ${theme.colors.black}` : 'none'}; + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.white}; + color: ${({ variant }) => + variant === 'black' ? theme.colors.white : theme.colors.black}; + + &:hover { + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.lightGray}; + } +`; diff --git a/src/pages/mypage-author-page/components/accountSettings/account/index.tsx b/src/pages/mypage-author-page/components/accountSettings/account/index.tsx new file mode 100644 index 0000000..cd80700 --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/account/index.tsx @@ -0,0 +1,69 @@ +import { useState } from 'react'; +import { + ButtonContainer, + FormContainer, + InputContainer, + InputField, + SectionTitle, + StyledButton, +} from './index.style'; +import { useSaveAuthorBankInfo } from '@/pages/mypage-author-page/hooks/useSaveAuthorBankInfo'; + +export const Account = () => { + const [bankName, setBankName] = useState(''); + const [accountHolder, setAccountHolder] = useState(''); + const [accountNumber, setAccountNumber] = useState(''); + const { mutate: saveBankInfo, isPending } = useSaveAuthorBankInfo(); + + const handleSave = () => { + if (!bankName || !accountHolder || !accountNumber) { + alert('모든 필드를 입력해주세요.'); + return; + } + + saveBankInfo({ + bank_name: bankName, + account_holder: accountHolder, + account_number: accountNumber, + }); + }; + + return ( + + 계좌 관리 + + + + setBankName(e.target.value)} + /> + + + + setAccountHolder(e.target.value)} + /> + + + + setAccountNumber(e.target.value)} + /> + + + + 취소 + + {isPending ? '저장 중...' : '저장하기'} + + + + ); +}; diff --git a/src/pages/mypage-author-page/components/accountSettings/basicInfo/index.style.ts b/src/pages/mypage-author-page/components/accountSettings/basicInfo/index.style.ts new file mode 100644 index 0000000..dfef5b6 --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/basicInfo/index.style.ts @@ -0,0 +1,201 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + width: 1080px; + margin: 0 auto; + background-color: ${theme.colors.white}; + + h1 { + margin: 0; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + padding-bottom: 20px; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const SectionTitle = styled.span` + ${theme.typography['20']} + font-weight: 600; + color: ${theme.colors.black}; +`; + +export const AccountInfo = styled.section` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 38px; + padding: 64px 0 48px 140px; + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const ProfileImageContainer = styled.div` + position: relative; + + width: 100px; + height: 100px; + + svg { + position: absolute; + right: 0; + bottom: 0; + } +`; + +export const ProfileImage = styled.div` + width: 100px; + height: 100px; + border-radius: 50%; + background-color: ${theme.colors.lightGray}; +`; + +export const UserDetails = styled.div` + display: flex; + flex-direction: column; + gap: 32px; + margin-top: calc(50px - 38px); + + div { + display: flex; + width: 257px; + height: 20px; + gap: 17px; + align-items: center; + } + + span { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.black}; + } +`; + +export const InputContainer = styled.section` + display: flex; + width: 800px; + height: auto; + flex-direction: column; + align-items: flex-start; + gap: 20px; + padding: 64px 0 50px 140px; + border-bottom: 1px solid ${theme.colors.lineLightColor}; + + label { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + } +`; + +export const BasicField = styled.div` + display: flex; + align-items: center; + gap: 17px; + + .name { + width: 120px; + margin-bottom: 12px; + + ${theme.typography['14']} + font-weight: 400; + } + + span { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + } +`; + +export const PhotoField = styled.div` + display: inline-flex; + width: 497px; + height: 120px; + align-items: flex-start; + gap: 17px; + + label { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + } +`; + +export const InputField = styled.div` + display: flex; + align-items: center; + gap: 17px; + + input { + width: 629px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } + + .address-container { + display: flex; + flex-direction: column; + gap: 10px; + + .primary-address { + display: flex; + align-items: center; + gap: 10px; + + input { + width: 512px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } + } + + input:last-child { + width: 631px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } + } +`; + +export const AddressButton = styled.button` + display: flex; + justify-content: center; + align-items: center; + width: 107px; + padding: 14px 16px; + ${theme.typography['16']} + font-weight: 600; + cursor: pointer; + background-color: ${theme.colors.white}; + border: 1px solid ${theme.colors.black}; +`; + +export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` + width: 106px; + padding: 12px 16px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + border: ${({ variant }) => + variant === 'white' ? `1px solid ${theme.colors.lightGray}` : 'none'}; + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.white}; + color: ${({ variant }) => + variant === 'black' ? theme.colors.white : theme.colors.black}; +`; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: center; + gap: 8px; + margin-top: 48px; +`; diff --git a/src/pages/mypage-author-page/components/accountSettings/basicInfo/index.tsx b/src/pages/mypage-author-page/components/accountSettings/basicInfo/index.tsx new file mode 100644 index 0000000..59aef90 --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/basicInfo/index.tsx @@ -0,0 +1,150 @@ +import { useState } from 'react'; + +import { + AccountInfo, + AddressButton, + BasicField, + ButtonContainer, + FormContainer, + InputContainer, + InputField, + ProfileImage, + ProfileImageContainer, + SectionTitle, + StyledButton, + UserDetails, +} from './index.style'; + +import IconCamera from '@assets/svg/Icon_Camera.svg'; +import EditButton from '@assets/svg/Icon_Edit.svg?react'; +import { + PhotoFieldContainer, + PhotoUploadBox, + Placeholder, +} from '../../managingProfiles/introduce/index.style'; + +export const BasicInfo = () => { + const [authorPhoto, setAuthorPhoto] = useState(null); + const [introPhoto, setIntroPhoto] = useState(null); + + const handleImageUpload = ( + event: React.ChangeEvent, + setImage: React.Dispatch> + ) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = () => { + setImage(reader.result as string); + }; + reader.readAsDataURL(file); + } + }; + + return ( + +

계정 설정

+ {/* TODO[찬영] - 로그인 API Response 데이터 연결 */} + + 계정 정보 + + + + + +
+ 닉네임 + 홍길동 +
+
+ 이메일 + artné@gmail.com +
+
+ 타입 + 작가 +
+
+
+ + 기본 정보 + +
이름
+
홍길동
+
+ + 휴대전화 + 010-1234-1234 + + + + + + + {/* 작가 사진 */} + + + + handleImageUpload(e, setAuthorPhoto)} + /> + {authorPhoto ? ( + 작가 사진 미리보기 + ) : ( + 카메라 아이콘 + )} + + + 작가 사진 최적 사이즈는 000*000입니다 + + + + {/* 소개 사진 */} + + + + handleImageUpload(e, setIntroPhoto)} + /> + {introPhoto ? ( + 소개 사진 미리보기 + ) : ( + 카메라 아이콘 + )} + + + 소개 사진 최적 사이즈는 000*000입니다 + + + + + + + + + +
+
+ + {/* TODO[찬영] - 주소찾기 버튼 클릭시 검색 페이지로 이동 */} + 주소찾기 +
+ +
+
+
+ + 취소 + 저장하기 + +
+ ); +}; diff --git a/src/pages/mypage-author-page/components/accountSettings/index.style.ts b/src/pages/mypage-author-page/components/accountSettings/index.style.ts new file mode 100644 index 0000000..d1b1dc6 --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/index.style.ts @@ -0,0 +1,12 @@ +import styled from '@emotion/styled'; + +export const TabBoxWrapper = styled.div` + display: flex; + margin-right: 100px; +`; + +export const AccountSettingWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; +`; diff --git a/src/pages/mypage-author-page/components/accountSettings/index.tsx b/src/pages/mypage-author-page/components/accountSettings/index.tsx new file mode 100644 index 0000000..a985dcb --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/index.tsx @@ -0,0 +1,46 @@ +import { useState } from 'react'; +import { BasicInfo } from './basicInfo'; +import { Account } from './account'; +import { Withdraw } from './withdraw'; + +import { MyPageSideBar } from '@/components/common/MyPageSideBar'; + +import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; + +export const AccountSettings = () => { + const [selectedTab, setSelectedTab] = useState< + '기본정보 관리' | '계좌 관리' | '회원 탈퇴' + >('기본정보 관리'); + + // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 + const renderSelectedTab = () => { + switch (selectedTab) { + case '기본정보 관리': + return ; + case '계좌 관리': + return ; + case '회원 탈퇴': + return ; + default: + return null; + } + }; + + return ( + + {/* 메뉴 선택 컴포넌트 */} + + + + + {/* 선택된 메뉴에 따라 컴포넌트 변경 */} + {renderSelectedTab()} + + ); +}; + +export default AccountSettings; diff --git a/src/pages/mypage-author-page/components/accountSettings/withdraw/index.style.ts b/src/pages/mypage-author-page/components/accountSettings/withdraw/index.style.ts new file mode 100644 index 0000000..e68f23b --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/withdraw/index.style.ts @@ -0,0 +1,48 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + width: 1080px; + height: 56px; + background-color: ${theme.colors.white}; +`; + +export const SectionTitle = styled.h1` + margin: 0; + ${theme.typography['24']} + font-weight: 600; + padding-bottom: 20px; + border-bottom: 2px solid ${theme.colors.black}; +`; + +export const ButtonContainer = styled.div` + display: flex; + width: 100%; + padding: 48px 0 104px; + justify-content: center; + margin-top: 315px; + + gap: 8px; + + border-top: 1px solid ${theme.colors.lineLightColor}; + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` + width: 106px; + padding: 10px 20px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + border: ${({ variant }) => + variant === 'white' ? `1px solid ${theme.colors.black}` : 'none'}; + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.white}; + color: ${({ variant }) => + variant === 'black' ? theme.colors.white : theme.colors.black}; + + &:hover { + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.lightGray}; + } +`; diff --git a/src/pages/mypage-author-page/components/accountSettings/withdraw/index.tsx b/src/pages/mypage-author-page/components/accountSettings/withdraw/index.tsx new file mode 100644 index 0000000..a3cf8bd --- /dev/null +++ b/src/pages/mypage-author-page/components/accountSettings/withdraw/index.tsx @@ -0,0 +1,19 @@ +import { + ButtonContainer, + FormContainer, + SectionTitle, + StyledButton, +} from './index.style'; + +export const Withdraw = () => { + return ( + + 회원 탈퇴 + + + 취소 + 탈퇴하기 + + + ); +}; diff --git a/src/pages/mypage-author-page/components/managementWorks/artWorks/index.style.ts b/src/pages/mypage-author-page/components/managementWorks/artWorks/index.style.ts new file mode 100644 index 0000000..9726273 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/artWorks/index.style.ts @@ -0,0 +1,60 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const ArtWorksContainer = styled.div` + display: flex; + flex-direction: column; + width: 1080px; + height: auto; + + gap: 32px; + + h1 { + margin: 0; + + padding-bottom: 20px; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const ArtworkContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const ArtworkGrid = styled.div` + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 40px 20px; + padding-bottom: 64px; + border-bottom: 1px solid ${theme.colors.border}; +`; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: flex-end; + align-items: center; + margin-top: 8px; +`; + +export const StyledButton = styled.button` + width: 106px; + padding: 12px 16px; + ${theme.typography['14']} + font-weight: 600; + cursor: pointer; + border: 1px solid ${theme.colors.black}; + background-color: ${theme.colors.white}; + color: ${theme.colors.black}; + + svg { + width: 12px; + height: 12px; + margin-right: 2px; + } +`; diff --git a/src/pages/mypage-author-page/components/managementWorks/artWorks/index.tsx b/src/pages/mypage-author-page/components/managementWorks/artWorks/index.tsx new file mode 100644 index 0000000..b5a3c0a --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/artWorks/index.tsx @@ -0,0 +1,51 @@ +import { useNavigate } from 'react-router-dom'; +import IconPlus from '@assets/svg/Icon_Plus.svg?react'; +import { Artwork } from '@/components/common/ArtWork'; +import { + ArtworkContainer, + ArtworkGrid, + ArtWorksContainer, + ButtonContainer, + StyledButton, +} from './index.style'; + +import { useGetAuthorArtworksExhibitions } from '@/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions'; + +const ArtWorks = () => { + const navigate = useNavigate(); + const { data } = useGetAuthorArtworksExhibitions(); + + const artworks = data?.artworks || []; + + return ( + +

작품

+ + + {artworks.length > 0 ? ( + + {artworks.map((artwork) => ( + + ))} + + ) : ( +

등록된 작품이 없습니다.

+ )} +
+ + + navigate('/artwork-register')}> + + 작품 등록 + + +
+ ); +}; + +export default ArtWorks; diff --git a/src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.style.ts b/src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.style.ts new file mode 100644 index 0000000..9726273 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.style.ts @@ -0,0 +1,60 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const ArtWorksContainer = styled.div` + display: flex; + flex-direction: column; + width: 1080px; + height: auto; + + gap: 32px; + + h1 { + margin: 0; + + padding-bottom: 20px; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const ArtworkContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const ArtworkGrid = styled.div` + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 40px 20px; + padding-bottom: 64px; + border-bottom: 1px solid ${theme.colors.border}; +`; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: flex-end; + align-items: center; + margin-top: 8px; +`; + +export const StyledButton = styled.button` + width: 106px; + padding: 12px 16px; + ${theme.typography['14']} + font-weight: 600; + cursor: pointer; + border: 1px solid ${theme.colors.black}; + background-color: ${theme.colors.white}; + color: ${theme.colors.black}; + + svg { + width: 12px; + height: 12px; + margin-right: 2px; + } +`; diff --git a/src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.tsx b/src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.tsx new file mode 100644 index 0000000..5a7f7e7 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/auctioningWorks/index.tsx @@ -0,0 +1,49 @@ +import { useNavigate } from 'react-router-dom'; +import IconPlus from '@assets/svg/Icon_Plus.svg?react'; +import { Artwork } from '@/components/common/ArtWork'; +import { + ArtworkContainer, + ArtworkGrid, + ArtWorksContainer, + ButtonContainer, + StyledButton, +} from './index.style'; + +import { useGetAuthorArtworksExhibitions } from '@/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions'; + +const AuctioningWorks = () => { + const navigate = useNavigate(); + + const { data } = useGetAuthorArtworksExhibitions(); + + const auction_artworks = data?.auction_artworks || []; + + return ( + +

경매 중인 작품

+ + + + {auction_artworks.map((auctionArtwork) => ( + + ))} + + + + + navigate('/auction-register')}> + + 경매 등록 + + +
+ ); +}; + +export default AuctioningWorks; diff --git a/src/pages/mypage-author-page/components/managementWorks/exhibitions/index.style.ts b/src/pages/mypage-author-page/components/managementWorks/exhibitions/index.style.ts new file mode 100644 index 0000000..44e8d40 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/exhibitions/index.style.ts @@ -0,0 +1,60 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const ArtWorksContainer = styled.div` + display: flex; + flex-direction: column; + width: 1080px; + height: auto; + + gap: 32px; + + h1 { + margin: 0; + + padding-bottom: 20px; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const ArtworkContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const ArtworkGrid = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 40px 20px; + padding-bottom: 64px; + border-bottom: 1px solid ${theme.colors.border}; +`; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: flex-end; + align-items: center; + margin-top: 8px; +`; + +export const StyledButton = styled.button` + width: 106px; + padding: 12px 16px; + ${theme.typography['14']} + font-weight: 600; + cursor: pointer; + border: 1px solid ${theme.colors.black}; + background-color: ${theme.colors.white}; + color: ${theme.colors.black}; + + svg { + width: 12px; + height: 12px; + margin-right: 2px; + } +`; diff --git a/src/pages/mypage-author-page/components/managementWorks/exhibitions/index.tsx b/src/pages/mypage-author-page/components/managementWorks/exhibitions/index.tsx new file mode 100644 index 0000000..c543026 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/exhibitions/index.tsx @@ -0,0 +1,56 @@ +import { useNavigate } from 'react-router-dom'; +import IconPlus from '@assets/svg/Icon_Plus.svg?react'; +import { Artwork } from '@/components/common/ArtWork'; + +import { + ArtworkContainer, + ArtworkGrid, + ArtWorksContainer, + ButtonContainer, + StyledButton, +} from './index.style'; + +import { useGetAuthorArtworksExhibitions } from '@/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions'; + +const Exhibitions = () => { + const navigate = useNavigate(); + + const { data } = useGetAuthorArtworksExhibitions(); + + const artworks = data?.artworks || []; + + const exhibitions = data?.exhibitions || []; + + return ( + +

진행 중인 전시

+ + + + {exhibitions.map((exhibition) => { + const relatedArtwork = artworks.find( + (artwork) => artwork.id === exhibition.id + ); + return ( + + ); + })} + + + + + navigate('/exhibit-register')}> + + 전시 등록 + + +
+ ); +}; + +export default Exhibitions; diff --git a/src/pages/mypage-author-page/components/managementWorks/index.style.ts b/src/pages/mypage-author-page/components/managementWorks/index.style.ts new file mode 100644 index 0000000..d1b1dc6 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/index.style.ts @@ -0,0 +1,12 @@ +import styled from '@emotion/styled'; + +export const TabBoxWrapper = styled.div` + display: flex; + margin-right: 100px; +`; + +export const AccountSettingWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; +`; diff --git a/src/pages/mypage-author-page/components/managementWorks/index.tsx b/src/pages/mypage-author-page/components/managementWorks/index.tsx new file mode 100644 index 0000000..f229b20 --- /dev/null +++ b/src/pages/mypage-author-page/components/managementWorks/index.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import ArtWorks from './artWorks'; +import AuctioningWorks from './auctioningWorks'; +import Exhibitions from './exhibitions'; + +import { MyPageSideBar } from '@/components/common/MyPageSideBar'; +import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; + +export const AccountSettings = () => { + const [selectedTab, setSelectedTab] = useState< + '작품' | '경매 중인 작품' | '진행 중인 전시' + >('작품'); + + // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 + const renderSelectedTab = () => { + switch (selectedTab) { + case '작품': + return ; + case '경매 중인 작품': + return ; + case '진행 중인 전시': + return ; + default: + return null; + } + }; + + return ( + + {/* 메뉴 선택 컴포넌트 */} + + + + + {/* 선택된 메뉴에 따라 컴포넌트 변경 */} + {renderSelectedTab()} + + ); +}; + +export default AccountSettings; diff --git a/src/pages/mypage-author-page/components/managingProfiles/allInfo/index.style.ts b/src/pages/mypage-author-page/components/managingProfiles/allInfo/index.style.ts new file mode 100644 index 0000000..eec89d1 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/allInfo/index.style.ts @@ -0,0 +1,134 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + display: flex; + flex-direction: column; + width: 1080px; + height: auto; + + gap: 32px; + + h1 { + margin: 0; + + padding-bottom: 20px; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const SectionTitle = styled.span` + ${theme.typography['20']} + font-weight: 600; + color: ${theme.colors.black}; +`; + +export const AccountInfo = styled.section` + display: flex; + align-items: flex-start; + padding: 32px 0 32px 32px; + gap: 24px; + border: 1px solid ${theme.colors.border}; +`; + +export const ProfileImageContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 108px; + height: 108px; +`; + +export const ProfileImage = styled.img` + width: 100px; + height: 100px; + border-radius: 50%; + background-color: ${theme.colors.lightGray}; +`; + +export const UserDetails = styled.div` + display: flex; + flex-direction: column; + padding: 20px 0; + + gap: 6px; + + div { + display: inline-flex; + width: 103px; + height: 34px; + gap: 6px; + align-items: flex-end; + } + + h2 { + margin: 0; + + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + } + + span { + display: flex; + ${theme.typography['20']} + font-weight: 400; + color: ${theme.colors.black}; + } + + p { + margin: 0; + + ${theme.typography['18']} + font-weight: 400; + color: ${theme.colors.fontGray}; + } +`; + +export const IntroduceContainer = styled.section` + display: inline-flex; + flex-direction: column; + + padding: 28px 120px 28px 32px; + align-items: flex-start; + gap: 18px; + border: 1px solid ${theme.colors.border}; +`; + +export const Content = styled.span` + display: flex; + align-items: center; + width: 928px; + + ${theme.typography['16']} + font-weight: 400; + color: ${theme.colors.font03gray}; +`; + +export const Table = styled.table` + width: 100%; + border-collapse: collapse; +`; + +export const Th = styled.th` + padding: 16px; + background-color: ${theme.colors.priceBox}; + ${theme.typography['16']} + font-weight: 400; + color: ${theme.colors.black}; +`; + +export const Tr = styled.tr` + border-bottom: 1px solid ${theme.colors.border}; +`; + +export const Td = styled.td` + padding: 22px 16px; + text-align: center; + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.fontGray}; +`; diff --git a/src/pages/mypage-author-page/components/managingProfiles/allInfo/index.tsx b/src/pages/mypage-author-page/components/managingProfiles/allInfo/index.tsx new file mode 100644 index 0000000..915c123 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/allInfo/index.tsx @@ -0,0 +1,147 @@ +import { + AccountInfo, + FormContainer, + IntroduceContainer, + Content, + ProfileImage, + ProfileImageContainer, + SectionTitle, + UserDetails, + Table, + Th, + Tr, + Td, +} from './index.style'; + +import { useGetAuthorProfile } from '@/pages/mypage-author-page/hooks/useGetAuthorProfile'; + +export const AllInfo = () => { + // useGetAuthorProfile 훅을 사용하여 'default' 프로필 데이터 가져오기 + const { data: authorData } = useGetAuthorProfile('default'); + + if (!authorData) { + return

데이터를 불러올 수 없습니다.

; + } + + const { + author_name, + author_image_url, + email, + description, + education, + award, + experience, + } = authorData; + + return ( + +

전체보기

+ {/* 작가 프로필 조회(전체보기) API 연결 */} + + + + + +
+

{author_name}

+ 작가 +
+
+

{email}

+
+
+
+ + 자기 소개 + {description} + + + 학력 정보 + + + + + + + + + + + + {education.map((edu, index) => ( + + + + + + + + ))} + +
학교전공상태입학일졸업일
+ {edu.school} + {edu.major}{edu.status}{edu.start_date}{edu.end_date}
+
+ + 수상 경력 + + + + + + + + + {award.map((award, index) => ( + + + + + ))} + +
기간수상 내용
{award.date} + {award.description} +
+
+ + 전시 및 프로젝트 경험 + + + + + + + + + {experience.map((experience, index) => ( + + + + + ))} + +
기간전시 내용
{experience.date} + {experience.description} +
+
+
+ ); +}; diff --git a/src/pages/mypage-author-page/components/managingProfiles/index.style.ts b/src/pages/mypage-author-page/components/managingProfiles/index.style.ts new file mode 100644 index 0000000..d1b1dc6 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/index.style.ts @@ -0,0 +1,12 @@ +import styled from '@emotion/styled'; + +export const TabBoxWrapper = styled.div` + display: flex; + margin-right: 100px; +`; + +export const AccountSettingWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; +`; diff --git a/src/pages/mypage-author-page/components/managingProfiles/index.tsx b/src/pages/mypage-author-page/components/managingProfiles/index.tsx new file mode 100644 index 0000000..0f7cd15 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/index.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { AllInfo } from './allInfo'; +import { Introduce } from './introduce'; +import { MyInformation } from './myInformation'; + +import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; +import { MyPageSideBar } from '@/components/common/MyPageSideBar'; + +export const ManagingProfiles = () => { + const [selectedTab, setSelectedTab] = useState< + '전체보기' | '자기 소개' | '자기 정보' + >('전체보기'); + + // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 + const renderSelectedTab = () => { + switch (selectedTab) { + case '전체보기': + return ; + case '자기 소개': + return ; + case '자기 정보': + return ; + default: + return null; + } + }; + + return ( + + {/* 메뉴 선택 컴포넌트 */} + + + + + {/* 선택된 메뉴에 따라 컴포넌트 변경 */} + {renderSelectedTab()} + + ); +}; + +export default ManagingProfiles; diff --git a/src/pages/mypage-author-page/components/managingProfiles/introduce/index.style.ts b/src/pages/mypage-author-page/components/managingProfiles/introduce/index.style.ts new file mode 100644 index 0000000..9e3ae95 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/introduce/index.style.ts @@ -0,0 +1,124 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + display: flex; + flex-direction: column; + width: 1080px; + height: auto; + + gap: 32px; + + h1 { + margin: 0; + + padding-bottom: 20px; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const SectionTitleBox = styled.section` + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; +`; + +export const SectionTitle = styled.h2` + display: flex; + text-align: center; + margin: 0; + ${theme.typography['20']} + font-weight: 600; +`; + +export const EditButton = styled.button` + display: flex; + padding: 10px 20px; + justify-content: center; + align-items: center; + gap: 4px; + border-radius: 100px; + + background-color: ${theme.colors.EditButton}; + border: none; + cursor: pointer; + + span { + ${theme.typography['13']} + font-weight: 500; + color: ${theme.colors.black}; + } +`; + +export const IntroduceContainer = styled.section` + display: inline-flex; + flex-direction: column; + + width: 1014px; + padding: 20px 32px; + align-items: flex-start; + gap: 12px; + border: 1px solid ${theme.colors.border}; +`; + +export const Content = styled.span` + display: flex; + align-items: center; + width: 928px; + + ${theme.typography['16']} + font-weight: 400; + color: ${theme.colors.font03gray}; +`; + +export const PhotoFieldContainer = styled.div` + display: flex; + width: 497px; +`; + +export const PhotoUploadBox = styled.div` + position: relative; + width: 120px; + height: 120px; + margin: 0 12px 0 17px; + + display: flex; + align-items: center; + justify-content: center; + + background: rgba(0, 0, 0, 0.2); + cursor: pointer; + overflow: hidden; + + input { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + cursor: pointer; + } + + .preview { + width: 120px; + height: 120px; + object-fit: cover; + } + + .icon { + width: 60px; + height: 60px; + } +`; + +export const Placeholder = styled.span` + align-self: flex-end; + width: 250px; + + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.fontGray}; +`; diff --git a/src/pages/mypage-author-page/components/managingProfiles/introduce/index.tsx b/src/pages/mypage-author-page/components/managingProfiles/introduce/index.tsx new file mode 100644 index 0000000..622cfd2 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/introduce/index.tsx @@ -0,0 +1,52 @@ +import { + IntroduceContainer, + FormContainer, + SectionTitle, + Content, + SectionTitleBox, + EditButton, +} from './index.style'; + +import EditIcon from '@assets/svg/Icon_Edit.svg?react'; + +import { useGetAuthorProfile } from '@/pages/mypage-author-page/hooks/useGetAuthorProfile'; + +export const Introduce = () => { + // useGetAuthorProfile 훅을 사용하여 'intro' 프로필 데이터 가져오기 + const { data: authorData } = useGetAuthorProfile('intro'); + + if (!authorData) { + return

데이터를 불러올 수 없습니다.

; + } + + const { description, work_style } = authorData; + + return ( + + {/* 작가 프로필 조회(자기소개) API 연결 */} +

자기 소개

+ + + + 작가 설명 + + 편집하기 + + + + {description} + + + + + 작업 스타일 + + 편집하기 + + + + {work_style} + +
+ ); +}; diff --git a/src/pages/mypage-author-page/components/managingProfiles/myInformation/index.style.ts b/src/pages/mypage-author-page/components/managingProfiles/myInformation/index.style.ts new file mode 100644 index 0000000..ec78ef1 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/myInformation/index.style.ts @@ -0,0 +1,89 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + display: flex; + flex-direction: column; + width: 1080px; + height: auto; + + gap: 32px; + + h1 { + margin: 0; + + padding-bottom: 20px; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const SectionTitleBox = styled.section` + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; +`; + +export const SectionTitle = styled.h2` + margin: 0; + ${theme.typography['20']} + font-weight: 600; +`; + +export const EditButton = styled.button` + display: flex; + padding: 10px 20px; + justify-content: center; + align-items: center; + gap: 4px; + border-radius: 100px; + + background-color: ${theme.colors.EditButton}; + border: none; + cursor: pointer; + + span { + ${theme.typography['13']} + font-weight: 500; + color: ${theme.colors.black}; + } +`; + +export const IntroduceContainer = styled.section` + display: inline-flex; + flex-direction: column; + + width: 1014px; + padding: 28px 32px; + align-items: flex-start; + gap: 24px; + border: 1px solid ${theme.colors.border}; +`; + +export const Table = styled.table` + width: 100%; + border-collapse: collapse; +`; + +export const Th = styled.th` + padding: 16px; + background-color: ${theme.colors.priceBox}; + ${theme.typography['16']} + font-weight: 400; + color: ${theme.colors.black}; +`; + +export const Tr = styled.tr` + border-bottom: 1px solid ${theme.colors.border}; +`; + +export const Td = styled.td` + padding: 22px 16px; + text-align: center; + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.fontGray}; +`; diff --git a/src/pages/mypage-author-page/components/managingProfiles/myInformation/index.tsx b/src/pages/mypage-author-page/components/managingProfiles/myInformation/index.tsx new file mode 100644 index 0000000..e487f00 --- /dev/null +++ b/src/pages/mypage-author-page/components/managingProfiles/myInformation/index.tsx @@ -0,0 +1,146 @@ +import { + EditButton, + FormContainer, + IntroduceContainer, + SectionTitle, + SectionTitleBox, + Table, + Td, + Th, + Tr, +} from './index.style'; +import FallbackUI from '@/components/common/FallbackUI'; + +import EditIcon from '@assets/svg/Icon_Edit.svg?react'; + +import { useGetAuthorProfile } from '@/pages/mypage-author-page/hooks/useGetAuthorProfile'; + +export const MyInformation = () => { + // useGetAuthorProfile 훅을 사용하여 'info' 프로필 데이터 가져오기 + const { data: authorData, isLoading } = useGetAuthorProfile('info'); + + if (isLoading) { + return ; + } + + if (!authorData) { + return

데이터를 불러올 수 없습니다.

; + } + + const { education, award, experience } = authorData; + + return ( + +

자기 정보

+ + {/* 작가 프로필 조회(자기정보) API 연결 */} + + + 작가 설명 + + 편집하기 + + + + + + + + + + + + + + + {education.map((edu, index) => ( + + + + + + + + ))} + +
학교전공상태입학일졸업일
+ {edu.school} + {edu.major}{edu.status}{edu.start_date}{edu.end_date}
+
+ + + + 작가 설명 + + 편집하기 + + + + + + + + + + + + {award.map((award, index) => ( + + + + + ))} + +
기간수상 내용
{award.date} + {award.description} +
+
+ + + + 작가 설명 + + 편집하기 + + + + + + + + + + + + {experience.map((experience, index) => ( + + + + + ))} + +
기간전시 내용
{experience.date} + {experience.description} +
+
+
+ ); +}; diff --git a/src/pages/mypage-author-page/components/menuChooser/index.style.ts b/src/pages/mypage-author-page/components/menuChooser/index.style.ts new file mode 100644 index 0000000..a5f1977 --- /dev/null +++ b/src/pages/mypage-author-page/components/menuChooser/index.style.ts @@ -0,0 +1,29 @@ +import styled from '@emotion/styled'; +import theme from '@styles/theme.ts'; + +export const Wrapper = styled.div` + display: flex; + width: 1080px; + align-items: center; + + background-color: ${theme.colors.white}; + border: 1px solid ${theme.colors.lineLightColor}; +`; + +export const MenuButton = styled.button<{ $isActive: boolean }>` + width: 100%; + padding: 16px; + border: none; + &:hover { + cursor: pointer; + } + + ${theme.typography['16']} + text-align: center; + text-overflow: ellipsis; + background-color: ${({ $isActive, theme }) => + $isActive ? theme.colors.black : 'transparent'}; + color: ${({ $isActive, theme }) => + $isActive ? theme.colors.white : theme.colors.font03gray}; + transition: background-color 0.2s ease, color 0.2s ease; +`; diff --git a/src/pages/mypage-author-page/components/menuChooser/index.tsx b/src/pages/mypage-author-page/components/menuChooser/index.tsx new file mode 100644 index 0000000..0ffc31a --- /dev/null +++ b/src/pages/mypage-author-page/components/menuChooser/index.tsx @@ -0,0 +1,44 @@ +import { Wrapper, MenuButton } from './index.style'; + +interface MenuChooserProps { + menus: T[]; + selectedMenu: T; + onSelectMenu: (menu: T) => void; +} + +/** + * 마이페이지 내 메뉴 선택 컴포넌트입니다. 기본 선택 메뉴는 '마이페이지' 입니다. + * `menus`를 props로 받아 다른 페이지에서도 활용 가능하도록 작성하였습니다. + * 컴포넌트 타입을 제네릭으로 설정해 확장성과 타입 안전성을 높였습니다. + * @param {string[]} menus - 메뉴 목록 + * @param {string} selectedMenu - 현재 선택된 메뉴 + * @param {(menu: string) => void} onSelectMenu - 선택된 메뉴를 부모 컴포넌트에 전달하는 콜백 함수 + * @returns {JSX.Element} 메뉴 버튼 리스트를 렌더링하는 컴포넌트 + * + * @example + * @author 노찬영 + */ + +export const MenuChooser = ({ + menus, + selectedMenu, + onSelectMenu, +}: MenuChooserProps) => { + return ( + + {menus.map((menu) => ( + onSelectMenu(menu)} + > + {menu} + + ))} + + ); +}; diff --git a/src/pages/mypage-author-page/components/menuMyPage/Auction/index.style.ts b/src/pages/mypage-author-page/components/menuMyPage/Auction/index.style.ts new file mode 100644 index 0000000..705694f --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/Auction/index.style.ts @@ -0,0 +1,61 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const AuctionContainer = styled.div` + display: flex; + width: 1080px; + flex-direction: column; + align-items: flex-start; + color: ${theme.colors.black}; + + h1 { + ${theme.typography['24']} + font-weight: 600; + } +`; + +export const Table = styled.table` + width: 100%; + border-collapse: collapse; + margin-top: 4px; + ${theme.typography['14']} + font-weight: 400; + padding: 22px 16px; + border-top: 2px solid ${theme.colors.black}; +`; + +export const TableHeader = styled.th` + padding: 22px 16px; + border-bottom: 1px solid ${theme.colors.fontGray}; + text-align: center; + ${theme.typography['14']} + font-weight: 600; + + &:nth-of-type(1) { + width: 630px; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4) { + width: 150px; + } +`; + +export const TableRow = styled.tr` + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const TableCell = styled.td` + padding: 22px 16px; + + &:nth-of-type(1) { + text-align: left; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4) { + text-align: center; + } +`; diff --git a/src/pages/mypage-author-page/components/menuMyPage/Auction/index.tsx b/src/pages/mypage-author-page/components/menuMyPage/Auction/index.tsx new file mode 100644 index 0000000..29a4fd3 --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/Auction/index.tsx @@ -0,0 +1,62 @@ +import { + AuctionContainer, + Table, + TableCell, + TableHeader, + TableRow, +} from './index.style'; + +import { useGetAuthorMypage } from '@/pages/mypage-author-page/hooks/useGetAuthorMypage'; + +/** + * @description 작가의 경매 내역을 표시하는 컴포넌트 + * @author 노찬영 + */ + +export const AuthorAuction = () => { + const { userMypageData } = useGetAuthorMypage(); + + // 작가의 경매 내역 가져오기 + const auctions = userMypageData.auctions; + + return ( + +

경매

+ + + + 작품정보 + 경매정보 + 금액 + 진행상황 + + + + {auctions.map((auction) => ( + + {`${auction.auction.artwork.title} - ${auction.auction.artwork.author.author_name}`} + + {new Date(auction.bid_date).toLocaleDateString('ko-KR')} + + + {new Intl.NumberFormat('ko-KR', { + style: 'currency', + currency: 'KRW', + }).format(auction.bid_price)} + + + {auction.status === 'BID' + ? '경매완료' + : auction.status === 'PARTICIPATE' + ? '입찰중' + : auction.status} + + + ))} + +
+
+ ); +}; + +export default AuthorAuction; diff --git a/src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.style.ts b/src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.style.ts new file mode 100644 index 0000000..5b6e6c8 --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.style.ts @@ -0,0 +1,71 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const MyCollectionContainer = styled.div` + width: 1080px; + height: auto; + display: flex; + flex-direction: column; + gap: 32px; + color: ${theme.colors.black}; + + h1 { + margin: 0; + padding: 70px 0 20px; + ${theme.typography['24']} + font-weight: 600; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const ArtworkContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const SectionTitle = styled.h2` + margin: 0; + ${theme.typography['20']} + font-weight: 600; +`; + +export const ArtworkGrid = styled.div` + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 40px 20px; + padding: 18px 0 64px; + border-bottom: 1px solid ${theme.colors.borderBottom}; +`; + +export const ExhibitionContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const ExhibitionGrid = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 76px 21px; + margin: 19px 0 80px; +`; + +export const ExhibitionItem = styled.div` + width: 346px; + height: 260px; + + h3 { + color: ${theme.colors.black}; + ${theme.typography['14']} + font-weight: 600; + } +`; + +export const ExhibitionImage = styled.img` + width: 346px; + height: 260px; + object-fit: cover; +`; diff --git a/src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.tsx b/src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.tsx new file mode 100644 index 0000000..d011bfd --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/artworkCollection/index.tsx @@ -0,0 +1,61 @@ +import { + MyCollectionContainer, + SectionTitle, + ArtworkGrid, + ExhibitionGrid, + ExhibitionItem, + ExhibitionImage, + ArtworkContainer, + ExhibitionContainer, +} from './index.style'; + +import { Artwork } from '@/components/common/ArtWork'; + +import { useGetAuthorMypage } from '@/pages/mypage-author-page/hooks/useGetAuthorMypage'; + +const ArtworkCollection = () => { + const { userMypageData } = useGetAuthorMypage(); + + const artworks = userMypageData.storage.artworks; + const exhibitions = userMypageData.storage.exhibitions; + + return ( + +

작품 보관함

+ + + 작품 + + {artworks.map((artwork) => ( + + ))} + + + + + 전시 + + {exhibitions.map((exhibition) => ( + + +

{exhibition.title}

+
+ ))} +
+
+
+ ); +}; + +export default ArtworkCollection; diff --git a/src/pages/mypage-author-page/components/menuMyPage/index.style.ts b/src/pages/mypage-author-page/components/menuMyPage/index.style.ts new file mode 100644 index 0000000..c290eaf --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/index.style.ts @@ -0,0 +1,27 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const MyPageWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; + background-color: ${theme.colors.white}; +`; + +export const ProfileContainer = styled.div` + display: flex; + flex-direction: column; + width: 250px; + margin-right: 100px; + background-color: ${theme.colors.white}; +`; + +export const MyPageContainer = styled.div` + display: flex; + flex-direction: column; + flex-grow: 1; + max-width: 1080px; + gap: 80px; + background-color: ${theme.colors.white}; +`; + diff --git a/src/pages/mypage-author-page/components/menuMyPage/index.tsx b/src/pages/mypage-author-page/components/menuMyPage/index.tsx new file mode 100644 index 0000000..c722c93 --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/index.tsx @@ -0,0 +1,30 @@ +import AuthorAuction from './Auction'; +import ArtworkCollection from './artworkCollection'; +import AuthorProfile from './profile'; +import { + MyPageContainer, + MyPageWrapper, + ProfileContainer, +} from './index.style'; + +export const MenuMyPage = ({ + setSelectedMenu, +}: { + setSelectedMenu: ( + menu: '마이페이지' | '프로필 관리' | '계정설정' | '작품/전시 관리' + ) => void; +}) => { + return ( + + + setSelectedMenu('계정설정')} /> + + + + + + + ); +}; + +export default MenuMyPage; diff --git a/src/pages/mypage-author-page/components/menuMyPage/profile/index.style.ts b/src/pages/mypage-author-page/components/menuMyPage/profile/index.style.ts new file mode 100644 index 0000000..851ef8d --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/profile/index.style.ts @@ -0,0 +1,82 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const ProfileContainer = styled.div` + display: flex; + width: 250px; + height: 260px; + flex-direction: column; + justify-content: center; + align-items: center; + + border: 1px solid ${theme.colors.profileBox}; +`; + +export const ProfileImage = styled.img` + width: 80px; + height: 80px; + border-radius: 50%; + object-fit: fill; +`; + +export const ProfileInfo = styled.div` + display: flex; + flex-direction: column; + align-items: center; + margin-top: 16px; +`; + +export const Name = styled.span` + ${(theme) => theme.theme.typography['18']} + font-weight: 600; + color: ${theme.colors.black}; +`; + +export const EditButton = styled.button` + display: flex; + width: 180px; + padding: 10px 20px; + justify-content: space-between; + align-items: center; + margin-top: 24px; + border-radius: 100px; + border: none; + cursor: pointer; + + ${theme.typography['13']} + font-weight: 400; + background-color: ${theme.colors.profileButton}; + color: ${theme.colors.font03gray}; +`; + +export const PaymentStatus = styled.div` + display: flex; + width: 202px; + flex-direction: column; + align-items: flex-start; + padding: 28px 4px 0; + gap: 20px; + margin-top: 28px; + border-top: 1px solid ${theme.colors.profileBox}; +`; + +export const PaymentItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + width: 202px; + height: 20px; +`; + +export const PaymentLabel = styled.span` + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.black}; +`; + +export const PaymentCount = styled.span` + text-align: right; + ${theme.typography['14']} + font-weight: 500; + color: ${theme.colors.black}; +`; diff --git a/src/pages/mypage-author-page/components/menuMyPage/profile/index.tsx b/src/pages/mypage-author-page/components/menuMyPage/profile/index.tsx new file mode 100644 index 0000000..0eee464 --- /dev/null +++ b/src/pages/mypage-author-page/components/menuMyPage/profile/index.tsx @@ -0,0 +1,45 @@ +import { + ProfileContainer, + ProfileImage, + ProfileInfo, + Name, + EditButton, +} from './index.style'; +import RightArrow from '@assets/svg/right-arrow.svg?react'; +import NoneProfile from '@assets/svg/Icon_Profile.svg'; + +import { useGetAuthorMypage } from '@/pages/mypage-author-page/hooks/useGetAuthorMypage'; + +interface AuthorProfileProps { + onEditProfile: () => void; +} + +/** + * 작가 프로필 컴포넌트입니다. + * 프로필 사진, 작가 이름, 작가 소속, 기본정보 수정 버튼을 표시합니다. + * @param {() => void} onEditProfile - 작가 계정 정보 수정 + * @author 노찬영 + **/ + +export const AuthorProfile = ({ onEditProfile }: AuthorProfileProps) => { + const { userMypageData } = useGetAuthorMypage(); + + const author = userMypageData.author; + + return ( + + + + {author.name} + + 기본정보 수정 + + + + ); +}; + +export default AuthorProfile; diff --git a/src/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions.ts b/src/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions.ts new file mode 100644 index 0000000..5d61707 --- /dev/null +++ b/src/pages/mypage-author-page/hooks/useGetAuthorArtworksExhibitions.ts @@ -0,0 +1,28 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getAuthorArtworksExhibitionsQuery } from '@/constants/queryKeys'; +import { TAuthorArtworksExhibitions } from '@/apis/mypageAuthor/type'; +import { handleError } from '@/utils/handleError'; +/** + * 작가의 작품, 경매 작품, 전시 정보를 가져오는 커스텀 훅 + * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. + * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. + * + * @returns {object} data, isLoading, error 상태 반환 + * @example const { data } = useGetAuthorArtworksExhibitions(); + * @author 노찬영, 홍규진 + **/ +export const useGetAuthorArtworksExhibitions = () => { + const { data, isLoading, error } = + useSuspenseQuery({ + queryKey: getAuthorArtworksExhibitionsQuery().queryKey, + queryFn: getAuthorArtworksExhibitionsQuery().queryFn, + staleTime: 1000 * 60 * 30, // 30분 + gcTime: 1000 * 60 * 60, // 1시간 + }); + + if (error) { + handleError(error); + } + + return { data, isLoading, error }; +}; diff --git a/src/pages/mypage-author-page/hooks/useGetAuthorMypage.ts b/src/pages/mypage-author-page/hooks/useGetAuthorMypage.ts new file mode 100644 index 0000000..18a76bc --- /dev/null +++ b/src/pages/mypage-author-page/hooks/useGetAuthorMypage.ts @@ -0,0 +1,30 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { TArtistMypage } from '@/apis/mypageAuthor/myPage/type'; +import { getAuthorMypageQuery } from '@/constants/queryKeys'; +import { handleError } from '@/utils/handleError'; +/** + * 작가의 마이페이지 정보를 가져오는 커스텀 훅 + * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. + * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. + * @returns {object} userMypageData, isLoading, error 상태 반환 + * @author 노찬영, 홍규진 + */ + +export const useGetAuthorMypage = () => { + const { + data: userMypageData, + isLoading, + error, + } = useSuspenseQuery({ + queryKey: getAuthorMypageQuery().queryKey, + queryFn: getAuthorMypageQuery().queryFn, + staleTime: 1000 * 60 * 30, // 30분 + gcTime: 1000 * 60 * 60, // 1시간 + }); + + if (error) { + handleError(error); + } + + return { userMypageData, isLoading, error }; +}; diff --git a/src/pages/mypage-author-page/hooks/useGetAuthorProfile.ts b/src/pages/mypage-author-page/hooks/useGetAuthorProfile.ts new file mode 100644 index 0000000..6c5d229 --- /dev/null +++ b/src/pages/mypage-author-page/hooks/useGetAuthorProfile.ts @@ -0,0 +1,27 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getAuthorProfileQuery } from '@/constants/queryKeys'; +import { TAuthorProfile, AuthorProfileType } from '@/apis/mypageAuthor/type'; +import { handleError } from '@/utils/handleError'; +/** + * 작가 프로필 조회를 위한 React Query 훅 + * @param type - 조회할 프로필 타입 ('default', 'intro', 'info') + * @returns 작가 프로필 데이터와 쿼리 상태 반환 + * @example + * const { data, isLoading, error } = useGetAuthorProfile('intro'); + * @author 노찬영, 홍규진 + */ +export const useGetAuthorProfile = (type: AuthorProfileType) => { + const { data, isLoading, error } = useSuspenseQuery({ + queryKey: getAuthorProfileQuery(type).queryKey, + queryFn: getAuthorProfileQuery(type).queryFn, + staleTime: 1000 * 60 * 30, // 30분 + gcTime: 1000 * 60 * 60, // 1시간 + retry: 1, // 실패 시 1회 재시도 + }); + + if (error) { + handleError(error); + } + + return { data, isLoading, error }; +}; diff --git a/src/pages/mypage-author-page/hooks/useSaveAuthorBankInfo.ts b/src/pages/mypage-author-page/hooks/useSaveAuthorBankInfo.ts new file mode 100644 index 0000000..d5c7e59 --- /dev/null +++ b/src/pages/mypage-author-page/hooks/useSaveAuthorBankInfo.ts @@ -0,0 +1,25 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { saveAuthorBankInfoMutation } from '@/constants/queryKeys'; +import { toast } from 'sonner'; + +/** + * 작가 계좌 정보 저장을 위한 커스텀 뮤테이션 훅 + * @returns 뮤테이션 객체와 상태(onSuccess, onError 핸들링 포함) + * @author 노찬영, 홍규진 + */ +export const useSaveAuthorBankInfo = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationKey: saveAuthorBankInfoMutation().mutationKey, + mutationFn: saveAuthorBankInfoMutation().mutationFn, + onSuccess: () => { + // 저장 성공 시, 관련 쿼리 캐시 무효화 + queryClient.invalidateQueries({ queryKey: ['authorProfile'] }); + toast.success('계좌 정보가 성공적으로 저장되었습니다.'); + }, + onError: (error) => { + toast.error(`계좌 정보 저장 중 오류 발생: ${error.message}`); + }, + }); +}; diff --git a/src/pages/mypage-author-page/hooks/useUpdateAuthorInfo.ts b/src/pages/mypage-author-page/hooks/useUpdateAuthorInfo.ts new file mode 100644 index 0000000..1f5d084 --- /dev/null +++ b/src/pages/mypage-author-page/hooks/useUpdateAuthorInfo.ts @@ -0,0 +1,40 @@ +import { useMutation } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { updateAuthorInfo } from '@/apis/mypageAuthor/author'; +import { getUpdateAuthorInfoQueryKey } from '@/constants/queryKeys'; +import { getQueryClient } from '@/contexts/query/getQueryClient'; +import { TUpdateAuthorInfo } from '@/apis/mypageAuthor/type'; + +/** + * 작가 계정 정보 수정을 위한 React Query 훅 + * @returns mutate 함수와 상태를 반환하여 작가 계정 정보 수정 요청 가능 + * @author 노찬영, 홍규진 + **/ +export const useUpdateAuthorInfo = () => { + const queryClient = getQueryClient(); + + const { + mutate: updateAuthor, + isPending, + error, + } = useMutation< + void, + Error, + { authorId: number; authorInfo: TUpdateAuthorInfo } + >({ + mutationKey: getUpdateAuthorInfoQueryKey(), + mutationFn: ({ authorId, authorInfo }) => + updateAuthorInfo(authorId, authorInfo), + onSuccess: () => { + toast.success('계정 정보가 성공적으로 업데이트되었습니다.'); + queryClient.invalidateQueries({ + queryKey: getUpdateAuthorInfoQueryKey(), + }); + }, + onError: (error) => { + toast.error(`계정 정보 수정에 실패했습니다. ${error.message}`); + }, + }); + + return { updateAuthor, isPending, error }; +}; diff --git a/src/pages/mypage-author-page/hooks/useUpdateAuthorProfile.ts b/src/pages/mypage-author-page/hooks/useUpdateAuthorProfile.ts new file mode 100644 index 0000000..5b7d29e --- /dev/null +++ b/src/pages/mypage-author-page/hooks/useUpdateAuthorProfile.ts @@ -0,0 +1,36 @@ +import { useMutation } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { AxiosError } from 'axios'; +import { updateAuthorProfile } from '@/apis/mypageAuthor/profile'; +import { TUpdateAuthorProfile } from '@/apis/mypageAuthor/type'; +import { getUpdateAuthorProfileQueryKey } from '@/constants/queryKeys'; +import { getQueryClient } from '@/contexts/query/getQueryClient'; + +/** + * 작가 프로필 개별 정보를 수정하는 React Query 훅 + * @returns mutate 함수와 상태를 반환하여 작가 정보 수정 요청 가능 + * @author 노찬영, 홍규진 + **/ +export const useUpdateAuthorProfile = () => { + const queryClient = getQueryClient(); + + const { + mutate: updateProfile, + isPending, + error, + } = useMutation, TUpdateAuthorProfile>({ + mutationKey: getUpdateAuthorProfileQueryKey(), + mutationFn: updateAuthorProfile, + onSuccess: () => { + toast.success('작가 프로필 정보가 성공적으로 업데이트되었습니다.'); + queryClient.invalidateQueries({ + queryKey: getUpdateAuthorProfileQueryKey(), + }); + }, + onError: (error) => { + toast.error(`작가 프로필 수정에 실패했습니다. ${error.message}`); + }, + }); + + return { updateProfile, isPending, error }; +}; diff --git a/src/pages/mypage-author-page/index.style.ts b/src/pages/mypage-author-page/index.style.ts new file mode 100644 index 0000000..9f2512a --- /dev/null +++ b/src/pages/mypage-author-page/index.style.ts @@ -0,0 +1,19 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const MyPageWrapper = styled.div` + display: flex; + flex-direction: column; + width: auto; + margin: 0 245px auto; + align-items: flex-end; + margin-bottom: 200px; + background-color: ${theme.colors.white}; +`; + +export const MenuWrapper = styled.div` + display: flex; + justify-content: flex-end; + background-color: ${theme.colors.white}; + padding: 80px 220px 100px 0; +`; diff --git a/src/pages/mypage-author-page/index.tsx b/src/pages/mypage-author-page/index.tsx new file mode 100644 index 0000000..1313093 --- /dev/null +++ b/src/pages/mypage-author-page/index.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react'; +import { MenuChooser } from './components/menuChooser'; +import { PageLayout } from '@/components/common/PageLayout'; + +import MenuMyPage from './components/menuMyPage'; +import ManagingProfiles from './components/managingProfiles'; +import AccountSettings from './components/accountSettings'; +import ManagementWorks from './components/managementWorks'; + +import { MenuWrapper, MyPageWrapper } from './index.style.ts'; + +export const MypageAuthorPage = () => { + const [selectedMenu, setSelectedMenu] = useState< + '마이페이지' | '프로필 관리' | '계정설정' | '작품/전시 관리' + >('마이페이지'); + + // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 + const renderSelectedMenu = () => { + switch (selectedMenu) { + case '마이페이지': + return ; + case '프로필 관리': + return ; + case '계정설정': + return ; + case '작품/전시 관리': + return ; + default: + return null; + } + }; + + return ( + + {/* 메뉴 선택 컴포넌트 */} + + + + + {/* 선택된 메뉴에 따라 컴포넌트 변경 */} + {renderSelectedMenu()} + + ); +} diff --git a/src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.style.ts b/src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.style.ts new file mode 100644 index 0000000..39ea986 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.style.ts @@ -0,0 +1,187 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + width: 1080px; + margin: 0 auto; + background-color: ${theme.colors.white}; + + h1 { + margin: 0; + ${theme.typography['24']} + font-weight: 600; + color: ${theme.colors.black}; + padding-bottom: 20px; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const SectionTitle = styled.span` + ${theme.typography['20']} + font-weight: 600; + color: ${theme.colors.black}; +`; + +export const AccountInfo = styled.section` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 38px; + padding: 64px 0 48px 140px; + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const ProfileImageContainer = styled.div` + position: relative; + + width: 100px; + height: 100px; + + svg { + position: absolute; + right: 0; + bottom: 0; + } +`; + +export const ProfileImage = styled.img` + width: 100px; + height: 100px; + border-radius: 50%; + background-color: ${theme.colors.lightGray}; +`; + +export const UserDetails = styled.div` + display: flex; + flex-direction: column; + gap: 32px; + margin-top: calc(50px - 38px); + + div { + display: flex; + width: 257px; + height: 20px; + gap: 17px; + align-items: center; + } + + span { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.black}; + } +`; + +export const InputContainer = styled.section` + display: flex; + width: 800px; + height: 432px; + flex-direction: column; + align-items: flex-start; + gap: 20px; + padding: 64px 0 50px 140px; + border-bottom: 1px solid ${theme.colors.lineLightColor}; + + label { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + } +`; + +export const BasicField = styled.div` + display: flex; + align-items: center; + gap: 17px; + + .name { + width: 120px; + margin-bottom: 12px; + + ${theme.typography['14']} + font-weight: 400; + } + + span { + width: 120px; + ${theme.typography['14']} + font-weight: 400; + } +`; + +export const InputField = styled.div` + display: flex; + align-items: center; + gap: 17px; + + input { + width: 629px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } + + .address-container { + display: flex; + flex-direction: column; + gap: 10px; + + .primary-address { + display: flex; + align-items: center; + gap: 10px; + + input { + width: 512px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } + } + + input:last-child { + width: 631px; + padding: 14px 16px; + border: 1px solid ${theme.colors.lightGray}; + ${theme.typography['16']} + font-weight: 400; + } + } +`; + +export const AddressButton = styled.button` + display: flex; + justify-content: center; + align-items: center; + width: 107px; + padding: 14px 16px; + ${theme.typography['16']} + font-weight: 600; + cursor: pointer; + background-color: ${theme.colors.white}; + border: 1px solid ${theme.colors.black}; +`; + +export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` + width: 106px; + padding: 12px 16px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + border: ${({ variant }) => + variant === 'white' ? `1px solid ${theme.colors.lightGray}` : 'none'}; + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.white}; + color: ${({ variant }) => + variant === 'black' ? theme.colors.white : theme.colors.black}; +`; + +export const ButtonContainer = styled.div` + display: flex; + justify-content: center; + gap: 8px; + margin-top: 48px; +`; diff --git a/src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.tsx b/src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.tsx new file mode 100644 index 0000000..c79f6b3 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/accountSettings/basicInfo/index.tsx @@ -0,0 +1,116 @@ +import { useState } from 'react'; +import { + AccountInfo, + AddressButton, + BasicField, + ButtonContainer, + FormContainer, + InputContainer, + InputField, + ProfileImage, + ProfileImageContainer, + SectionTitle, + StyledButton, + UserDetails, +} from './index.style'; +import DefaultImage from '@assets/svg/Icon_Profile.svg'; +import EditButton from '@assets/svg/Icon_Edit.svg?react'; + +import { useGetBuyerMypage } from '@/pages/mypage-buyer-page/hooks/useGetBuyerMypage'; +import { useUpdateUserInfo } from '@/pages/mypage-buyer-page/hooks/useUpdateUserInfo'; + +export const BasicInfo = () => { + const { userMypageData } = useGetBuyerMypage(); + + // 사용자 데이터 객체 추출 + const { profile_image_url, name } = userMypageData.buyer; + + const [nickname, setNickname] = useState(''); + const [birth, setBirth] = useState(''); + const [address, setAddress] = useState(''); + + const { updateUser, isPending } = useUpdateUserInfo(); + + const handleSave = () => { + updateUser({ nickname, birth, address }); + }; + + return ( + +

계정 설정

+ + + 계정 정보 + + + + + +
+ 닉네임 + {nickname} +
+
+ 이메일 + artné@gmail.com +
+
+ 타입 + 작품 구매자 +
+
+
+ + + 기본 정보 + +
이름
+
{name || '사용자 이름'}
+
+ + 휴대전화 + 010-1234-1234 + + + + setNickname(e.target.value)} + /> + + + + setBirth(e.target.value)} + /> + + + +
+
+ setAddress(e.target.value)} + /> + 주소찾기 +
+ +
+
+
+ + + 취소 + + {isPending ? '저장 중...' : '저장하기'} + + +
+ ); +}; diff --git a/src/pages/mypage-buyer-page/components/accountSettings/index.style.ts b/src/pages/mypage-buyer-page/components/accountSettings/index.style.ts new file mode 100644 index 0000000..d1b1dc6 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/accountSettings/index.style.ts @@ -0,0 +1,12 @@ +import styled from '@emotion/styled'; + +export const TabBoxWrapper = styled.div` + display: flex; + margin-right: 100px; +`; + +export const AccountSettingWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; +`; diff --git a/src/pages/mypage-buyer-page/components/accountSettings/index.tsx b/src/pages/mypage-buyer-page/components/accountSettings/index.tsx new file mode 100644 index 0000000..7d6bf6e --- /dev/null +++ b/src/pages/mypage-buyer-page/components/accountSettings/index.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react'; +import { BasicInfo } from './basicInfo'; +import { Withdraw } from './withdraw'; +import { AccountSettingWrapper, TabBoxWrapper } from './index.style'; +import { MyPageSideBar } from '@/components/common/MyPageSideBar'; + +export const AccountSettings = () => { + const [selectedTab, setSelectedTab] = useState<'기본정보 관리' | '회원 탈퇴'>( + '기본정보 관리' + ); + + // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 + const renderSelectedTab = () => { + switch (selectedTab) { + case '기본정보 관리': + return ; + case '회원 탈퇴': + return ; + default: + return null; + } + }; + + return ( + + {/* 메뉴 선택 컴포넌트 */} + + + + + {/* 선택된 메뉴에 따라 컴포넌트 변경 */} + {renderSelectedTab()} + + ); +}; + +export default AccountSettings; diff --git a/src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.style.ts b/src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.style.ts new file mode 100644 index 0000000..e68f23b --- /dev/null +++ b/src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.style.ts @@ -0,0 +1,48 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const FormContainer = styled.div` + width: 1080px; + height: 56px; + background-color: ${theme.colors.white}; +`; + +export const SectionTitle = styled.h1` + margin: 0; + ${theme.typography['24']} + font-weight: 600; + padding-bottom: 20px; + border-bottom: 2px solid ${theme.colors.black}; +`; + +export const ButtonContainer = styled.div` + display: flex; + width: 100%; + padding: 48px 0 104px; + justify-content: center; + margin-top: 315px; + + gap: 8px; + + border-top: 1px solid ${theme.colors.lineLightColor}; + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const StyledButton = styled.button<{ variant: 'black' | 'white' }>` + width: 106px; + padding: 10px 20px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + border: ${({ variant }) => + variant === 'white' ? `1px solid ${theme.colors.black}` : 'none'}; + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.white}; + color: ${({ variant }) => + variant === 'black' ? theme.colors.white : theme.colors.black}; + + &:hover { + background-color: ${({ variant }) => + variant === 'black' ? theme.colors.black : theme.colors.lightGray}; + } +`; diff --git a/src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.tsx b/src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.tsx new file mode 100644 index 0000000..a3cf8bd --- /dev/null +++ b/src/pages/mypage-buyer-page/components/accountSettings/withdraw/index.tsx @@ -0,0 +1,19 @@ +import { + ButtonContainer, + FormContainer, + SectionTitle, + StyledButton, +} from './index.style'; + +export const Withdraw = () => { + return ( + + 회원 탈퇴 + + + 취소 + 탈퇴하기 + + + ); +}; diff --git a/src/pages/mypage-buyer-page/components/menuChooser/index.style.ts b/src/pages/mypage-buyer-page/components/menuChooser/index.style.ts new file mode 100644 index 0000000..a5f1977 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuChooser/index.style.ts @@ -0,0 +1,29 @@ +import styled from '@emotion/styled'; +import theme from '@styles/theme.ts'; + +export const Wrapper = styled.div` + display: flex; + width: 1080px; + align-items: center; + + background-color: ${theme.colors.white}; + border: 1px solid ${theme.colors.lineLightColor}; +`; + +export const MenuButton = styled.button<{ $isActive: boolean }>` + width: 100%; + padding: 16px; + border: none; + &:hover { + cursor: pointer; + } + + ${theme.typography['16']} + text-align: center; + text-overflow: ellipsis; + background-color: ${({ $isActive, theme }) => + $isActive ? theme.colors.black : 'transparent'}; + color: ${({ $isActive, theme }) => + $isActive ? theme.colors.white : theme.colors.font03gray}; + transition: background-color 0.2s ease, color 0.2s ease; +`; diff --git a/src/pages/mypage-buyer-page/components/menuChooser/index.tsx b/src/pages/mypage-buyer-page/components/menuChooser/index.tsx new file mode 100644 index 0000000..180b10a --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuChooser/index.tsx @@ -0,0 +1,42 @@ +import { Wrapper, MenuButton } from './index.style'; + +interface MenuChooserProps { + menus: T[]; + selectedMenu: T; + onSelectMenu: (menu: T) => void; +} + +/** + * 마이페이지 내 메뉴 선택 컴포넌트입니다. + * `menus`를 props로 받아 다른 페이지에서도 활용 가능하도록 작성하였습니다. + * 컴포넌트 타입을 제네릭으로 설정해 확장성과 타입 안전성을 높였습니다. + * @param {string[]} menus - 메뉴 목록 + * @param {string} selectedMenu - 현재 선택된 메뉴 + * @param {(menu: string) => void} onSelectMenu - 선택된 메뉴를 부모 컴포넌트에 전달하는 콜백 함수 + * + * @example + * @author 노찬영 + */ +export const MenuChooser = ({ + menus, + selectedMenu, + onSelectMenu, +}: MenuChooserProps) => { + return ( + + {menus?.map((menu) => ( + onSelectMenu(menu)} + > + {menu} + + ))} + + ); +}; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.style.ts b/src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.style.ts new file mode 100644 index 0000000..b714e90 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.style.ts @@ -0,0 +1,84 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const AuctionContainer = styled.div` + display: flex; + width: 1080px; + flex-direction: column; + align-items: flex-start; + color: ${theme.colors.black}; + + h1 { + ${theme.typography['24']} + font-weight: 600; + } +`; + +export const Table = styled.table` + width: 100%; + border-collapse: collapse; + margin-top: 4px; + ${theme.typography['14']} + font-weight: 400; + padding: 22px 16px; + border-top: 2px solid ${theme.colors.black}; +`; + +export const TableHeader = styled.th` + padding: 22px 16px; + border-bottom: 1px solid ${theme.colors.fontGray}; + text-align: center; + ${theme.typography['14']} + font-weight: 600; + + &:nth-of-type(1) { + width: 630px; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4) { + width: 150px; + } +`; + +export const TableRow = styled.tr` + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const TableCell = styled.td` + &:nth-of-type(1) { + text-align: left; + padding: 22px 16px; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4), + &:nth-of-type(5) { + text-align: center; + padding: 22px 16px; + } + + &:nth-of-type(5) { + width: 76px; + padding: 19px 8px; + text-align: center; + } +`; + +export const AuctionButton = styled.button` + width: 100%; + display: flex; + padding: 7px 6px; + justify-content: center; + align-items: center; + + border: none; + border-radius: 2px; + cursor: pointer; + background-color: ${theme.colors.font03gray}; + color: ${theme.colors.white}; + ${theme.typography['14']} + font-weight: 400; +`; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.tsx b/src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.tsx new file mode 100644 index 0000000..790e2c4 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/Auction/index.tsx @@ -0,0 +1,74 @@ +import { + AuctionButton, + AuctionContainer, + Table, + TableCell, + TableHeader, + TableRow, +} from './index.style'; +import { useNavigate } from 'react-router-dom'; +import { useGetBuyerMypage } from '@/pages/mypage-buyer-page/hooks/useGetBuyerMypage'; + +/** + * @description 작품 구매자의 경매 내역을 표시하는 컴포넌트 + * @author 노찬영 + */ +export const ArtBuyerAuction = () => { + const { userMypageData } = useGetBuyerMypage(); + const navigate = useNavigate(); + + const { auctions } = userMypageData; + + const handleBtnClick = (auctionId: number) => { + navigate(`/auction/${auctionId}`); + }; + + // status 값을 한글로 변환하는 함수 + const getStatusText = (status: string) => { + const statusMap: Record = { + BID: '경매완료', + PARTICIPATE: '입찰중', + }; + return statusMap[status] || status; // 기본적으로 원래 상태값 유지 + }; + + return ( + +

경매

+ + + + 작품정보 + 경매 종료일 + 금액 + 진행상황 + + + + + {auctions?.map((auction) => ( + + {`${auction.auction.artwork.title} - ${auction.auction.artwork.author.author_name}`} + + {new Date(auction.bid_date).toLocaleDateString('ko-KR')} + + {`₩${auction.bid_price.toLocaleString()}`} + {getStatusText(auction.status)} + + {auction.status === 'PARTICIPATE' && ( + handleBtnClick(auction.auction_id)} + > + 입찰하기 + + )} + + + ))} + +
+
+ ); +}; + +export default ArtBuyerAuction; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.style.ts b/src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.style.ts new file mode 100644 index 0000000..5b6e6c8 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.style.ts @@ -0,0 +1,71 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const MyCollectionContainer = styled.div` + width: 1080px; + height: auto; + display: flex; + flex-direction: column; + gap: 32px; + color: ${theme.colors.black}; + + h1 { + margin: 0; + padding: 70px 0 20px; + ${theme.typography['24']} + font-weight: 600; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const ArtworkContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const SectionTitle = styled.h2` + margin: 0; + ${theme.typography['20']} + font-weight: 600; +`; + +export const ArtworkGrid = styled.div` + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 40px 20px; + padding: 18px 0 64px; + border-bottom: 1px solid ${theme.colors.borderBottom}; +`; + +export const ExhibitionContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const ExhibitionGrid = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 76px 21px; + margin: 19px 0 80px; +`; + +export const ExhibitionItem = styled.div` + width: 346px; + height: 260px; + + h3 { + color: ${theme.colors.black}; + ${theme.typography['14']} + font-weight: 600; + } +`; + +export const ExhibitionImage = styled.img` + width: 346px; + height: 260px; + object-fit: cover; +`; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.tsx b/src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.tsx new file mode 100644 index 0000000..f7da5cc --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/MyCollection/index.tsx @@ -0,0 +1,85 @@ +import { + MyCollectionContainer, + SectionTitle, + ArtworkGrid, + ExhibitionGrid, + ExhibitionItem, + ExhibitionImage, + ArtworkContainer, + ExhibitionContainer, +} from './index.style'; + +import { Artwork } from '@/components/common/ArtWork'; + +import { useNavigate } from 'react-router-dom'; +import { useGetBuyerMypage } from '@/pages/mypage-buyer-page/hooks/useGetBuyerMypage'; + +/** + * @description 작품 구매자의 작품과 전시를 표시하는 컴포넌트 + * @author 노찬영 + */ +const MyCollection = () => { + const navigate = useNavigate(); + const { userMypageData } = useGetBuyerMypage(); + + // 작품 구매자 작품 데이터 + const artworks = userMypageData?.myCollection?.artworks || []; + // 작품 구매자 전시 데이터 + const exhibitions = userMypageData?.myCollection?.exhibitions || []; + + // 작품 클릭 시 작품 상세 페이지로 이동 + const handleArtworkClick = (artworkId: number) => { + navigate(`/artwork/${artworkId}`); + }; + + // 전시 클릭 시 전시 상세 페이지로 이동 + const handleExhibitionClick = (exhibitionId: number) => { + navigate(`/exhibition/${exhibitionId}`); + }; + + return ( + +

마이컬렉션

+ + + 작품 + + {artworks.map((artwork) => ( + handleArtworkClick(artwork.id)} + /> + ))} + + + + + 전시 + + {exhibitions.map((exhibition) => ( + handleExhibitionClick(exhibition.id)} + style={{ cursor: 'pointer' }} + > + +

{exhibition.title}

+
+ ))} +
+
+
+ ); +}; + +export default MyCollection; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.style.ts b/src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.style.ts new file mode 100644 index 0000000..1a6471b --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.style.ts @@ -0,0 +1,86 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const PaymentContainer = styled.div` + display: flex; + width: 1080px; + flex-direction: column; + align-items: flex-start; + color: ${theme.colors.black}; + + h1 { + ${theme.typography['24']} + font-weight: 600; + } +`; + +export const Table = styled.table` + width: 100%; + border-collapse: collapse; + margin-top: 4px; + ${theme.typography['14']} + font-weight: 400; + padding: 22px 16px; + border-top: 2px solid ${theme.colors.black}; +`; + +export const TableHeader = styled.th` + border-bottom: 1px solid ${theme.colors.fontGray}; + text-align: center; + ${theme.typography['14']} + font-weight: 600; + padding: 22px 16px; + + &:nth-of-type(1) { + width: 630px; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4) { + width: 150px; + } + &:nth-of-type(5) { + width: 76px; + text-align: center; + } +`; + +export const TableRow = styled.tr` + border-bottom: 1px solid ${theme.colors.lineLightColor}; +`; + +export const TableCell = styled.td` + &:nth-of-type(1) { + padding: 22px 16px; + text-align: left; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4) { + padding: 22px 16px; + text-align: center; + } + + &:nth-of-type(5) { + padding: 19px 8px; + text-align: center; + } +`; + +export const PaymentButton = styled.button` + width: 100%; + display: flex; + padding: 7px 6px; + justify-content: center; + align-items: center; + + border: none; + border-radius: 2px; + cursor: pointer; + background-color: ${theme.colors.font03gray}; + color: ${theme.colors.white}; + ${theme.typography['14']} + font-weight: 400; +`; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.tsx b/src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.tsx new file mode 100644 index 0000000..457b446 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/Payment/index.tsx @@ -0,0 +1,70 @@ +import { + PaymentContainer, + Table, + TableHeader, + TableRow, + TableCell, + PaymentButton, +} from './index.style'; + +import { useGetBuyerMypage } from '@/pages/mypage-buyer-page/hooks/useGetBuyerMypage'; +import { useKakaoPay } from '@/pages/mypage-buyer-page/hooks/useKakaoPay'; + +/** + * @description 작품 구매자의 결제 내역을 표시하는 컴포넌트 + * @author 노찬영 + */ +export const Payment = () => { + const { userMypageData } = useGetBuyerMypage(); + const { initiatePayment } = useKakaoPay(); + + const { payments } = userMypageData; + + const handleBtnClick = (paymentId: number) => { + initiatePayment(paymentId); + }; + + const getPaymentStatus = (status: string) => { + if (status === 'COMPLETED') return '결제 완료'; + if (status === 'PENDING') return '결제 대기중'; + return status; + }; + + return ( + +

결제

+ + + + 작품정보 + 금액 + 입찰정보 + 진행상황 + + + + + {payments?.map((payment) => ( + + {`${payment.auction.artwork.title} - ${payment.auction.artwork.author.author_name}`} + {`₩${payment.payment_price.toLocaleString()}`} + + {new Date(payment.created_at).toLocaleDateString('ko-KR')} + + {getPaymentStatus(payment.payment_status)} + + {payment.payment_status === 'PENDING' && ( + handleBtnClick(payment.id)}> + 결제하기 + + )} + + + ))} + +
+
+ ); +}; + +export default Payment; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/index.style.ts b/src/pages/mypage-buyer-page/components/menuMyPage/index.style.ts new file mode 100644 index 0000000..c290eaf --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/index.style.ts @@ -0,0 +1,27 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const MyPageWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; + background-color: ${theme.colors.white}; +`; + +export const ProfileContainer = styled.div` + display: flex; + flex-direction: column; + width: 250px; + margin-right: 100px; + background-color: ${theme.colors.white}; +`; + +export const MyPageContainer = styled.div` + display: flex; + flex-direction: column; + flex-grow: 1; + max-width: 1080px; + gap: 80px; + background-color: ${theme.colors.white}; +`; + diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/index.tsx b/src/pages/mypage-buyer-page/components/menuMyPage/index.tsx new file mode 100644 index 0000000..4c1aed7 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/index.tsx @@ -0,0 +1,28 @@ +import ArtBuyerAuction from './Auction'; +import MyCollection from './MyCollection'; +import Payment from './Payment'; +import ArtBuyerProfile from './profile'; +import { + MyPageContainer, + MyPageWrapper, + ProfileContainer, +} from './index.style'; + +interface MenuMyPageProps { + setSelectedMenu: (menu: '마이페이지' | '계정설정' | '구매 작품') => void; +} + +export const MenuMyPage = ({ setSelectedMenu }: MenuMyPageProps) => { + return ( + + + setSelectedMenu('계정설정')} /> + + + + + + + + ); +}; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/profile/index.style.ts b/src/pages/mypage-buyer-page/components/menuMyPage/profile/index.style.ts new file mode 100644 index 0000000..14a4191 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/profile/index.style.ts @@ -0,0 +1,83 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const ProfileContainer = styled.div` + display: flex; + width: 210px; + height: 335px; + padding: 28px 20px; + flex-direction: column; + justify-content: center; + align-items: center; + + border: 1px solid ${theme.colors.profileBox}; +`; + +export const ProfileImage = styled.img` + width: 80px; + height: 80px; + border-radius: 50%; + object-fit: fill; +`; + +export const ProfileInfo = styled.div` + display: flex; + flex-direction: column; + align-items: center; + margin-top: 16px; +`; + +export const Name = styled.span` + ${(theme) => theme.theme.typography['18']} + font-weight: 600; + color: ${theme.colors.black}; +`; + +export const EditButton = styled.button` + display: flex; + width: 180px; + padding: 10px 20px; + justify-content: space-between; + align-items: center; + margin-top: 16px; + border-radius: 100px; + border: none; + cursor: pointer; + + ${theme.typography['13']} + font-weight: 400; + background-color: ${theme.colors.profileButton}; + color: ${theme.colors.font03gray}; +`; + +export const PaymentStatus = styled.div` + display: flex; + width: 202px; + flex-direction: column; + align-items: flex-start; + padding: 28px 4px 0; + gap: 20px; + margin-top: 28px; + border-top: 1px solid ${theme.colors.profileBox}; +`; + +export const PaymentItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + width: 202px; + height: 20px; +`; + +export const PaymentLabel = styled.span` + ${theme.typography['14']} + font-weight: 400; + color: ${theme.colors.black}; +`; + +export const PaymentCount = styled.span` + text-align: right; + ${theme.typography['14']} + font-weight: 500; + color: ${theme.colors.black}; +`; diff --git a/src/pages/mypage-buyer-page/components/menuMyPage/profile/index.tsx b/src/pages/mypage-buyer-page/components/menuMyPage/profile/index.tsx new file mode 100644 index 0000000..6b4f8c2 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/menuMyPage/profile/index.tsx @@ -0,0 +1,68 @@ +import { + Name, + ProfileContainer, + ProfileImage, + ProfileInfo, + EditButton, + PaymentStatus, + PaymentItem, + PaymentLabel, + PaymentCount, +} from './index.style'; +import RightArrow from '@assets/svg/right-arrow.svg?react'; +import NoneProfile from '@assets/svg/Icon_Profile.svg'; + +import { useGetBuyerMypage } from '@/pages/mypage-buyer-page/hooks/useGetBuyerMypage'; + +interface ArtBuyerProfileProps { + onEditProfile: () => void; +} + +/** + * 작품 구매자 프로필 컴포넌트입니다. + * 프로필 사진, 본인 이름, 기본정보 수정 버튼, 결제 대기/완료/수령 상태를 표시합니다. + * @param {() => void} onEditProfile - 작품 구매자 계정 정보 수정 + * @author 노찬영 + **/ + +export const ArtBuyerProfile = ({ onEditProfile }: ArtBuyerProfileProps) => { + const { userMypageData } = useGetBuyerMypage(); + + const { buyer, paymentCounts } = userMypageData; + const paymentData = paymentCounts?.[0] ?? { + pending: 0, + completed: 0, + received: 0, + }; + + return ( + + + + {buyer.name} + + 기본정보 수정 + + + + + 결제 대기중 + {paymentData.pending} + + + 결제 완료 + {paymentData.completed} + + + 수령 완료 + {paymentData.received} + + + + ); +}; + +export default ArtBuyerProfile; diff --git a/src/pages/mypage-buyer-page/components/purchasedWorks/index.style.ts b/src/pages/mypage-buyer-page/components/purchasedWorks/index.style.ts new file mode 100644 index 0000000..a06bcf6 --- /dev/null +++ b/src/pages/mypage-buyer-page/components/purchasedWorks/index.style.ts @@ -0,0 +1,70 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const PurchasedWorksContainer = styled.div` + width: 1080px; + height: auto; + display: flex; + flex-direction: column; + gap: 32px; + color: ${theme.colors.black}; + + h1 { + margin: 0; + padding: 70px 0 20px; + ${theme.typography['24']} + font-weight: 600; + border-bottom: 2px solid ${theme.colors.black}; + } +`; + +export const ArtworkContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const SectionTitle = styled.h2` + margin: 0; + ${theme.typography['20']} + font-weight: 600; +`; + +export const ArtworkGrid = styled.div` + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 40px 20px; + padding: 18px 0 64px; +`; + +export const ExhibitionContainer = styled.section` + display: flex; + width: 100%; + height: auto; + flex-direction: column; +`; + +export const ExhibitionGrid = styled.div` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 76px 21px; + margin: 19px 0 80px; +`; + +export const ExhibitionItem = styled.div` + width: 346px; + height: 260px; + + h3 { + color: ${theme.colors.black}; + ${theme.typography['14']} + font-weight: 600; + } +`; + +export const ExhibitionImage = styled.img` + width: 346px; + height: 260px; + object-fit: cover; +`; diff --git a/src/pages/mypage-buyer-page/components/purchasedWorks/index.tsx b/src/pages/mypage-buyer-page/components/purchasedWorks/index.tsx new file mode 100644 index 0000000..dc4cb0b --- /dev/null +++ b/src/pages/mypage-buyer-page/components/purchasedWorks/index.tsx @@ -0,0 +1,49 @@ +import { + PurchasedWorksContainer, + SectionTitle, + ArtworkGrid, + ArtworkContainer, +} from './index.style'; + +import { Artwork } from '@/components/common/ArtWork'; + +import { useGetBuyerMypage } from '../../hooks/useGetBuyerMypage'; +import { useNavigate } from 'react-router-dom'; + +const PurchasedWorks = () => { + const navigate = useNavigate(); + // 작품 클릭 시 작품 상세 페이지로 이동 + const handleArtworkClick = (artworkId: number) => { + navigate(`/artwork/${artworkId}`); + }; + + const { userMypageData } = useGetBuyerMypage(); + + const artworks = userMypageData.myCollection.artworks; + + return ( + +

구매 작품

+ + + 작품 + + {artworks?.map((artwork) => ( + handleArtworkClick(artwork.id)} + /> + ))} + + +
+ ); +}; + +export default PurchasedWorks; diff --git a/src/pages/mypage-buyer-page/hooks/useGetBuyerMypage.ts b/src/pages/mypage-buyer-page/hooks/useGetBuyerMypage.ts new file mode 100644 index 0000000..cae93a2 --- /dev/null +++ b/src/pages/mypage-buyer-page/hooks/useGetBuyerMypage.ts @@ -0,0 +1,28 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getBuyerMypageQuery } from '@/constants/queryKeys'; +import { TBuyerMypage } from '@/apis/mypageBuyer/myPage/type'; +import { handleError } from '@/utils/handleError'; +/** + * 특정 사용자의 마이페이지 정보를 가져오는 커스텀 훅 + * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. + * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. + * @returns {object} userMypageData, isLoading, error 상태 반환 + * @author 노찬영, 홍규진 + */ + +export const useGetBuyerMypage = () => { + const { + data: userMypageData, + isLoading, + error, + } = useSuspenseQuery({ + queryKey: getBuyerMypageQuery().queryKey, + queryFn: getBuyerMypageQuery().queryFn, + staleTime: 1000 * 60 * 30, // 30분 + gcTime: 1000 * 60 * 60, // 1시간 + }); + if (error) { + handleError(error); + } + return { userMypageData, isLoading, error }; +}; diff --git a/src/pages/mypage-buyer-page/hooks/useGetPurchasedArtworks.ts b/src/pages/mypage-buyer-page/hooks/useGetPurchasedArtworks.ts new file mode 100644 index 0000000..210df18 --- /dev/null +++ b/src/pages/mypage-buyer-page/hooks/useGetPurchasedArtworks.ts @@ -0,0 +1,29 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getPurchasedArtworksQuery } from '@/constants/queryKeys'; +import { TPurchasedArtwork } from '@/apis/mypageBuyer/type'; +import { handleError } from '@/utils/handleError'; +/** + * 구매한 작품 리스트를 가져오는 커스텀 훅 + * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. + * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. + * @returns {object} purchasedArtworksData, isLoading, error 상태 반환 + * @author 노찬영, 홍규진 + **/ +export const useGetPurchasedArtworks = () => { + const { + data: purchasedArtworksData, + isLoading, + error, + } = useSuspenseQuery({ + queryKey: getPurchasedArtworksQuery().queryKey, + queryFn: getPurchasedArtworksQuery().queryFn, + staleTime: 1000 * 60 * 60, // 1시간 + gcTime: 1000 * 60 * 30, // 30분 + }); + + if (error) { + handleError(error); + } + + return { purchasedArtworksData, isLoading, error }; +}; diff --git a/src/pages/mypage-buyer-page/hooks/useKakaoPay.ts b/src/pages/mypage-buyer-page/hooks/useKakaoPay.ts new file mode 100644 index 0000000..bb04271 --- /dev/null +++ b/src/pages/mypage-buyer-page/hooks/useKakaoPay.ts @@ -0,0 +1,59 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { + requestKakaoPayMutation, + approveKakaoPayMutation, +} from '@/constants/mutationKey'; +import { toast } from 'sonner'; +/** + * 카카오페이 결제 Hook + * @returns 카카오페이 결제 관련 함수 및 상태값 + * @author 노찬영, 홍규진 + */ + +export const useKakaoPay = () => { + const queryClient = useQueryClient(); + // 결제 준비 (결제 URL 요청) + const initiatePaymentMutation = useMutation({ + mutationKey: [...requestKakaoPayMutation().mutationKey], + mutationFn: (paymentId: number) => + requestKakaoPayMutation().mutationFn(paymentId), + onSuccess: (result) => { + toast.success('결제 준비 성공!'); + window.location.href = result.next_redirect_pc_url; // PC 결제창으로 이동 + queryClient.invalidateQueries({ + queryKey: requestKakaoPayMutation().successMutationKey, + }); + }, + onError: (error) => { + toast.error(`결제 준비에 실패했습니다. ${error.message}`); + }, + }); + + // 결제 승인 (pgToken을 받아 결제 승인 요청) + const confirmPaymentMutation = useMutation({ + mutationKey: approveKakaoPayMutation().mutationKey, + mutationFn: ({ + paymentId, + pgToken, + }: { + paymentId: number; + pgToken: string; + }) => approveKakaoPayMutation().mutationFn(paymentId, pgToken), + onSuccess: (approvedPaymentId) => { + toast.success( + `결제가 성공적으로 완료되었습니다. Payment ID: ${approvedPaymentId}` + ); + queryClient.invalidateQueries({ + queryKey: approveKakaoPayMutation().successMutationKey, + }); + }, + onError: (error) => { + toast.error(`결제 승인에 실패했습니다. ${error.message}`); + }, + }); + + return { + initiatePayment: initiatePaymentMutation.mutate, + confirmPayment: confirmPaymentMutation.mutate, + }; +}; diff --git a/src/pages/mypage-buyer-page/hooks/useUpdateUserInfo.ts b/src/pages/mypage-buyer-page/hooks/useUpdateUserInfo.ts new file mode 100644 index 0000000..74c1ffd --- /dev/null +++ b/src/pages/mypage-buyer-page/hooks/useUpdateUserInfo.ts @@ -0,0 +1,37 @@ +import { useMutation } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { updateUserInfoMutation } from '@/constants/mutationKey'; +import { getQueryClient } from '@/contexts/query/getQueryClient'; +import { TUpdateUserInfo } from '@/apis/mypageBuyer/type'; +const queryClient = getQueryClient(); + +/** + * 작품 구매자 계정 정보 수정을 위한 React Query 훅 + * @returns mutate 함수와 상태를 반환하여 사용자 정보 수정 요청 가능 + * @example + * const { mutate: updateUser } = useUpdateUserInfo(); + * updateUser({ nickname: '새 닉네임' }); + * @author 노찬영, 홍규진 + **/ +export const useUpdateUserInfo = () => { + const { + mutate: updateUser, + isPending, + error, + } = useMutation({ + mutationKey: updateUserInfoMutation().mutationKey, + mutationFn: (userInfo: TUpdateUserInfo) => + updateUserInfoMutation().mutationFn(userInfo), + onSuccess: () => { + toast.success('계정 정보가 성공적으로 업데이트되었습니다.'); + queryClient.invalidateQueries({ + queryKey: updateUserInfoMutation().successMutationKey, + }); + }, + onError: (error) => { + toast.error(`계정 정보 수정에 실패했습니다. ${error.message}`); + }, + }); + + return { updateUser, isPending, error }; +}; diff --git a/src/pages/mypage-buyer-page/index.style.ts b/src/pages/mypage-buyer-page/index.style.ts new file mode 100644 index 0000000..9f2512a --- /dev/null +++ b/src/pages/mypage-buyer-page/index.style.ts @@ -0,0 +1,19 @@ +import styled from '@emotion/styled'; +import theme from '@/styles/theme'; + +export const MyPageWrapper = styled.div` + display: flex; + flex-direction: column; + width: auto; + margin: 0 245px auto; + align-items: flex-end; + margin-bottom: 200px; + background-color: ${theme.colors.white}; +`; + +export const MenuWrapper = styled.div` + display: flex; + justify-content: flex-end; + background-color: ${theme.colors.white}; + padding: 80px 220px 100px 0; +`; diff --git a/src/pages/mypage-buyer-page/index.tsx b/src/pages/mypage-buyer-page/index.tsx new file mode 100644 index 0000000..cfe5f21 --- /dev/null +++ b/src/pages/mypage-buyer-page/index.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { MenuChooser } from './components/menuChooser/index.tsx'; +import { PageLayout } from '@/components/common/PageLayout'; + +import { MenuMyPage } from './components/menuMyPage/index.tsx'; +import AccountSettings from './components/accountSettings/index.tsx'; +import PurchasedWorks from './components/purchasedWorks/index.tsx'; + +import { MyPageWrapper, MenuWrapper } from './index.style.ts'; + +export const MypageBuyerPage = () => { + const [selectedMenu, setSelectedMenu] = useState< + '마이페이지' | '계정설정' | '구매 작품' + >('마이페이지'); + + // 선택된 메뉴에 따른 컴포넌트 렌더링 함수 + const renderSelectedMenu = () => { + switch (selectedMenu) { + case '마이페이지': + return ; + case '계정설정': + return ; + case '구매 작품': + return ; + default: + return null; + } + }; + + return ( + + {/* 메뉴 선택 컴포넌트 */} + + + + + {/* 선택된 메뉴에 따라 컴포넌트 변경 */} + {renderSelectedMenu()} + + ); +}; From 7b46927466d4fd7babce5d094573dac6702a303d Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:03:37 +0900 Subject: [PATCH 08/11] =?UTF-8?q?Refactor:=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/Error/DefaultErrorFallbackUI/index.tsx | 5 ++--- src/components/common/PageLayout/index.tsx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/common/Error/DefaultErrorFallbackUI/index.tsx b/src/components/common/Error/DefaultErrorFallbackUI/index.tsx index 3f942b8..5c10cdc 100644 --- a/src/components/common/Error/DefaultErrorFallbackUI/index.tsx +++ b/src/components/common/Error/DefaultErrorFallbackUI/index.tsx @@ -24,10 +24,9 @@ export default function DefaultErrorFallbackUI({ ⚠️ 잠시 문제가 발생했어요 + {error.name} {error.message} - - 다시 시도하기 - + 다시 시도하기 ); diff --git a/src/components/common/PageLayout/index.tsx b/src/components/common/PageLayout/index.tsx index b5fa3f8..f98e03c 100644 --- a/src/components/common/PageLayout/index.tsx +++ b/src/components/common/PageLayout/index.tsx @@ -29,7 +29,7 @@ export const PageLayout = ({ children }: PageLayoutProps) => { } function handleLinkMypageFn() { - navigate('/mypage/art-buyer'); + navigate('/mypage/buyer'); } return ( From 4e755055da73b51f37fa02dcb7fcf4b32dab7ebf Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:04:16 +0900 Subject: [PATCH 09/11] =?UTF-8?q?Refactor:=20Tanstack-Query=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85=EB=93=A4=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=ED=97=A8?= =?UTF-8?q?=EB=93=A4=EB=A7=81=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/MySpaceModal/index.tsx | 2 +- .../hooks/useGetArtworkDetail.ts | 16 ++++--- .../artwork-detail/hooks/useMySpaceForm.ts | 8 ++-- .../hooks/useToggleArtworkLike.ts | 12 +++--- .../hooks/useArtworkRegisterForm.ts | 43 +++++++++---------- src/pages/artwork/hooks/useGetArtworks.ts | 29 ++++++++++--- .../hooks/useGetAuctionDtail.ts | 12 ++---- .../auction-detail/hooks/usePostAuctionBid.ts | 22 +++------- src/pages/auction-detail/index.tsx | 7 --- .../hooks/useAuctionRegisterForm.ts | 15 ++++--- .../hooks/useGetAvailableArtwork.ts | 8 ++-- src/pages/auction/hooks/useAuctionLike.ts | 6 +-- src/pages/auction/hooks/useAuctionUnlike.ts | 8 ++-- src/pages/auction/hooks/useGetAuctionLists.ts | 12 ++---- .../author-detail/hooks/useGetAuthorDetail.ts | 9 +--- src/pages/author/hooks/useGetAuthorLists.ts | 12 ++---- .../exhibition/hooks/useGetExhibitionList.ts | 11 ++--- src/pages/login/hooks/useOauthLogin.ts | 5 ++- src/pages/main/hooks/useGetMainData.ts | 11 +++-- .../register/hooks/usePostAuthRegister.ts | 2 +- 20 files changed, 117 insertions(+), 133 deletions(-) diff --git a/src/pages/artwork-detail/components/MySpaceModal/index.tsx b/src/pages/artwork-detail/components/MySpaceModal/index.tsx index d32ca5d..664011d 100644 --- a/src/pages/artwork-detail/components/MySpaceModal/index.tsx +++ b/src/pages/artwork-detail/components/MySpaceModal/index.tsx @@ -22,7 +22,7 @@ import { } from './index.style.ts'; import { Text } from '@/styles/text'; import useMySpaceForm from '@/pages/artwork-detail/hooks/useMySpaceForm.ts'; -import { TMySpaceFormData } from '@/apis/artwork-detail/type'; +import { TMySpaceFormData } from '@/apis/artworkDetail/type.ts'; import Close from '@/assets/svg/icon-close.svg'; import Upload from '@/assets/svg/space-register.svg'; diff --git a/src/pages/artwork-detail/hooks/useGetArtworkDetail.ts b/src/pages/artwork-detail/hooks/useGetArtworkDetail.ts index 2618e6f..e4cf7e4 100644 --- a/src/pages/artwork-detail/hooks/useGetArtworkDetail.ts +++ b/src/pages/artwork-detail/hooks/useGetArtworkDetail.ts @@ -1,12 +1,12 @@ import { useSuspenseQuery } from '@tanstack/react-query'; -import { getArtworkDetail } from '@/apis/artwork-detail/artwork'; -import { TArtworkDetailResult } from '@/apis/artwork-detail/type'; - +import { TArtworkDetailResult } from '@/apis/artworkDetail/type'; +import { getArtworkDetailQuery } from '@/constants/queryKeys'; +import { handleError } from '@/utils/handleError'; /** * 특정 작품의 상세 정보를 가져오는 React Query 커스텀 훅 * @param artworkId 작품 ID * @returns {object} artworkData, isLoading, error 상태 반환 - * @author 김서윤 + * @author 김서윤, 홍규진 */ export const useGetArtworkDetail = (artworkId: number) => { const { @@ -14,11 +14,15 @@ export const useGetArtworkDetail = (artworkId: number) => { isLoading, error, } = useSuspenseQuery({ - queryKey: ['artworkDetail', artworkId], - queryFn: () => getArtworkDetail(artworkId), + queryKey: [...getArtworkDetailQuery(artworkId).queryKey], + queryFn: getArtworkDetailQuery(artworkId).queryFn, staleTime: 1000 * 60 * 30, // 30분 gcTime: 1000 * 60 * 15, // 15분 }); + if (error) { + handleError(error); + } + return { artworkData, isLoading, error }; }; diff --git a/src/pages/artwork-detail/hooks/useMySpaceForm.ts b/src/pages/artwork-detail/hooks/useMySpaceForm.ts index 8009811..8687ef4 100644 --- a/src/pages/artwork-detail/hooks/useMySpaceForm.ts +++ b/src/pages/artwork-detail/hooks/useMySpaceForm.ts @@ -1,9 +1,11 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { postMySpaceMutation } from '@/constants/mutationKey'; -import { TMySpaceFormData } from '@/apis/artwork-detail/type'; +import { TMySpaceFormData } from '@/apis/artworkDetail/type'; import { toast } from 'sonner'; import { useState } from 'react'; import { getAvailableArtworksQuery } from '@/constants/queryKeys'; +import { handleError } from '@/utils/handleError'; + const useMySpaceForm = () => { const queryClient = useQueryClient(); const [formData, setFormData] = useState({ @@ -39,12 +41,12 @@ const useMySpaceForm = () => { onSuccess: async () => { toast.success('공간 등록이 성공적으로 완료되었습니다.'); queryClient.invalidateQueries({ - queryKey: postMySpaceMutation().mutationKey, + queryKey: postMySpaceMutation().successMutationKey, }); await getAvailableArtworksQuery().queryFn(); }, onError: (error: Error) => { - toast.error(`공간 등록 실패: ${error.message}`); + handleError(error); }, }); diff --git a/src/pages/artwork-detail/hooks/useToggleArtworkLike.ts b/src/pages/artwork-detail/hooks/useToggleArtworkLike.ts index 83e303b..464fd2b 100644 --- a/src/pages/artwork-detail/hooks/useToggleArtworkLike.ts +++ b/src/pages/artwork-detail/hooks/useToggleArtworkLike.ts @@ -1,6 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { toggleArtworkLike } from '@/apis/artwork-like/like'; -import { TToggleLikeResult } from '@/apis/artwork-like/type'; +import { toggleArtworkLike } from '@/apis/artworkLike/like'; +import { TToggleLikeResult } from '@/apis/artworkLike/type'; import { toast } from 'sonner'; import { toggleArtworkLikeMutation } from '@/constants/mutationKey'; @@ -25,10 +25,12 @@ export const useToggleArtworkLike = () => { }, onSuccess: (data, { artworkId }) => { toast.success(data.message); - queryClient.invalidateQueries({ queryKey: ['artworkDetail', artworkId] }); + queryClient.invalidateQueries({ + queryKey: [...toggleArtworkLikeMutation(artworkId).mutationKey], + }); }, - onError: () => { - toast.error('좋아요를 변경하는 데 실패했습니다.'); + onError: (error: Error) => { + toast.error(`좋아요 변경에 실패했습니다. ${error.message}`); }, }); diff --git a/src/pages/artwork-register/hooks/useArtworkRegisterForm.ts b/src/pages/artwork-register/hooks/useArtworkRegisterForm.ts index 36a36e1..370e562 100644 --- a/src/pages/artwork-register/hooks/useArtworkRegisterForm.ts +++ b/src/pages/artwork-register/hooks/useArtworkRegisterForm.ts @@ -3,11 +3,31 @@ import { postArtworkRegisterMutation } from '@/constants/mutationKey'; import { TArtworkRegisterFormData } from '@/apis/artworkRegister/type'; import { toast } from 'sonner'; import { useState } from 'react'; -import { getAvailableArtworksQuery } from '@/constants/queryKeys'; import { useNavigate } from 'react-router-dom'; const useArtworkRegisterForm = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); + + /** + * 작품 등록 뮤테이션 + * @author 홍규진 + */ + const mutation = useMutation({ + mutationKey: postArtworkRegisterMutation().mutationKey, + mutationFn: (data: TArtworkRegisterFormData) => + postArtworkRegisterMutation().mutationFn(data), + onSuccess: async () => { + toast.success('작품 등록이 성공적으로 완료되었습니다.'); + queryClient.invalidateQueries({ + queryKey: postArtworkRegisterMutation().successMutationKey, + }); + navigate('/'); + }, + onError: (error: Error) => { + toast.error(`작품 등록 실패: ${error.message}`); + }, + }); + const [formData, setFormData] = useState({ images: [], theme: '', @@ -61,27 +81,6 @@ const useArtworkRegisterForm = () => { return true; }; - /** - * 작품 등록 뮤테이션 - * @author 홍규진 - */ - const mutation = useMutation({ - mutationKey: postArtworkRegisterMutation().mutationKey, - mutationFn: (data: TArtworkRegisterFormData) => - postArtworkRegisterMutation().mutationFn(data), - onSuccess: async () => { - toast.success('작품 등록이 성공적으로 완료되었습니다.'); - queryClient.invalidateQueries({ - queryKey: postArtworkRegisterMutation().mutationKey, - }); - await getAvailableArtworksQuery().queryFn(); - navigate('/'); - }, - onError: (error: Error) => { - toast.error(`작품 등록 실패: ${error.message}`); - }, - }); - /** * 작품 등록 폼 제출 함수 * 비어있는 것도 알려주기 diff --git a/src/pages/artwork/hooks/useGetArtworks.ts b/src/pages/artwork/hooks/useGetArtworks.ts index 8889afc..296b629 100644 --- a/src/pages/artwork/hooks/useGetArtworks.ts +++ b/src/pages/artwork/hooks/useGetArtworks.ts @@ -1,10 +1,10 @@ import { useSuspenseQuery } from '@tanstack/react-query'; -import { getArtworks } from '@/apis/artwork/artwork'; import { TArtWorkResult } from '@/apis/artwork/type'; - +import { getArtworkListQuery } from '@/constants/queryKeys'; +import { handleError } from '@/utils/handleError'; /** * 특정 작품의 상세 정보를 가져오는 React Query 커스텀 훅 - * @author 김서윤 + * @author 김서윤, 홍규진 */ export const useGetArtworks = ( page: number, @@ -26,12 +26,29 @@ export const useGetArtworks = ( isLoading, error, } = useSuspenseQuery({ - queryKey: ['artworks', page, pageSize, themes, sizes, forms, sortingKey], - queryFn: () => - getArtworks(page, pageSize, themes, sizes, forms, sortingKey), + queryKey: getArtworkListQuery( + page, + pageSize, + themes, + sizes, + forms, + sortingKey + ).queryKey, + queryFn: getArtworkListQuery( + page, + pageSize, + themes, + sizes, + forms, + sortingKey + ).queryFn, staleTime: 1000 * 60 * 30, gcTime: 1000 * 60 * 15, }); + if (error) { + handleError(error); + } + return { artworkData, isLoading, error }; }; diff --git a/src/pages/auction-detail/hooks/useGetAuctionDtail.ts b/src/pages/auction-detail/hooks/useGetAuctionDtail.ts index 5351853..b3ed126 100644 --- a/src/pages/auction-detail/hooks/useGetAuctionDtail.ts +++ b/src/pages/auction-detail/hooks/useGetAuctionDtail.ts @@ -1,9 +1,7 @@ import { getAuctionDetailQuery } from '@/constants/queryKeys'; import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; import { TGetAuctionDetailResponse } from '@/apis/auction/type'; - +import { handleError } from '@/utils/handleError'; /** * 경매 상세 정보를 가져오는 커스텀 훅 * React Query의 useSuspenseQuery를 활용해 데이터를 가져오고, 오류 발생 시 toast 알림을 띄움 @@ -11,7 +9,7 @@ import { TGetAuctionDetailResponse } from '@/apis/auction/type'; * @param {number} auctionId - 경매 ID * @returns {object} data, error 상태 반환 * @example const { data } = useGetAuctionDetail(145); - * @author 이하늘 + * @author 이하늘, 홍규진 **/ export const useGetAuctionDetail = (auctionId: number) => { const { data, isLoading, error } = @@ -23,11 +21,7 @@ export const useGetAuctionDetail = (auctionId: number) => { }); if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '경매 상세 정보를 불러오는데 실패했습니다.'; - toast.error(errorMessage); + handleError(error); } return { data, isLoading, error }; diff --git a/src/pages/auction-detail/hooks/usePostAuctionBid.ts b/src/pages/auction-detail/hooks/usePostAuctionBid.ts index 8a0db97..09da3b1 100644 --- a/src/pages/auction-detail/hooks/usePostAuctionBid.ts +++ b/src/pages/auction-detail/hooks/usePostAuctionBid.ts @@ -4,10 +4,10 @@ import { } from '@/apis/auction/type'; import { postAuctionBidMutation } from '@/constants/mutationKey'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import axios from 'axios'; import { useState } from 'react'; import { toast } from 'sonner'; +//TODO-[규진] 폼데이터 및 뮤테이션 관심사 분리 필요 export const usePostAuctionBid = () => { const [formData, setFormData] = useState({ auction_id: null, @@ -25,22 +25,11 @@ export const usePostAuctionBid = () => { onSuccess: (data: TPostAuctionBidResponse) => { toast.success(`입찰 성공: ${data.message}`); queryClient.invalidateQueries({ - queryKey: ['auctionDetails', data.current_price], + queryKey: postAuctionBidMutation().mutationKey, }); }, onError: (error: Error) => { - if (axios.isAxiosError(error)) { - // AxiosError인 경우 - if (error.response) { - // 서버 응답에서 message를 추출하여 사용 - const message = error.response.data.message; - toast.error(`입찰 실패: ${message}`); // 사용자에게 오류 메시지 표시 - } else { - toast.error('서버 응답을 받을 수 없습니다.'); - } - } else { - toast.error('입찰에 실패했습니다. 다시 시도해주세요.'); - } + toast.error(`입찰 실패: ${error.message}`); }, }); @@ -70,10 +59,9 @@ export const usePostAuctionBid = () => { if (validateForm(data)) { try { await mutation.mutateAsync(data); // 입찰 요청 - console.log('입찰 성공1'); + toast.success('입찰 성공'); } catch (error) { - console.error('입찰 실패', error); - alert('입찰에 실패했습니다. 다시 시도해주세요.'); + console.log(error); } } else { console.log('유효성 검사 실패'); diff --git a/src/pages/auction-detail/index.tsx b/src/pages/auction-detail/index.tsx index 05fc465..f866f68 100644 --- a/src/pages/auction-detail/index.tsx +++ b/src/pages/auction-detail/index.tsx @@ -77,13 +77,6 @@ export const AuctionDetail = () => { } }, [currentPrice]); - // 입찰 실패 시 오류 메시지 표시 - // useEffect(() => { - // if (isError && bidError?.response?.data?.message) { - // toast.error(`입찰 실패: ${bidError.response.data.message}`); - // } - // }, [isError, bidError]); - if (!auctionId) { return ( diff --git a/src/pages/auction-register/hooks/useAuctionRegisterForm.ts b/src/pages/auction-register/hooks/useAuctionRegisterForm.ts index ecfdf78..5b77fdd 100644 --- a/src/pages/auction-register/hooks/useAuctionRegisterForm.ts +++ b/src/pages/auction-register/hooks/useAuctionRegisterForm.ts @@ -3,10 +3,12 @@ import { TAuctionRegisterFormData } from '@/apis/auctionRegister/type'; import { toast } from 'sonner'; import { useState } from 'react'; import { postAuctionRegisterMutation } from '@/constants/mutationKey'; +import { useNavigate } from 'react-router-dom'; const useAuctionRegisterForm = () => { + const navigate = useNavigate(); const [formData, setFormData] = useState({ - artwork_id: null, - start_price: null, + artwork_id: 0, + start_price: 0, }); const queryClient = useQueryClient(); @@ -21,8 +23,9 @@ const useAuctionRegisterForm = () => { onSuccess: () => { toast.success('경매 등록이 성공적으로 완료되었습니다.'); queryClient.invalidateQueries({ - queryKey: postAuctionRegisterMutation().mutationKey, + queryKey: postAuctionRegisterMutation().successMutationKey, }); + navigate('/auction'); }, onError: (error: Error) => { toast.error(`경매 등록 실패: ${error.message}`); @@ -35,12 +38,12 @@ const useAuctionRegisterForm = () => { * @author 홍규진 */ const validateForm = (data: TAuctionRegisterFormData) => { - if (data.artwork_id === null) { + if (data.artwork_id === 0) { toast.error('작품을 선택해주세요.'); return false; } - if (data.start_price === null) { - toast.error('시작 금액을 입력해주세요.'); + if (data.start_price === 0) { + toast.error('시작 금액을 입력해주세요. 0원은 입력할 수 없습니다.'); return false; } return true; diff --git a/src/pages/auction-register/hooks/useGetAvailableArtwork.ts b/src/pages/auction-register/hooks/useGetAvailableArtwork.ts index d2d4ebf..77c4196 100644 --- a/src/pages/auction-register/hooks/useGetAvailableArtwork.ts +++ b/src/pages/auction-register/hooks/useGetAvailableArtwork.ts @@ -1,7 +1,7 @@ import { TGetAvailableArtworksResponse } from '@/apis/auctionRegister/type'; import { getAvailableArtworksQuery } from '@/constants/queryKeys'; -import { useQuery } from '@tanstack/react-query'; - +import { useSuspenseQuery } from '@tanstack/react-query'; +import { handleError } from '@/utils/handleError'; /** * 경매 가능 작품 조회 훅 * @author 홍규진 @@ -12,14 +12,14 @@ export const useGetAvailableArtwork = () => { isLoading, refetch, error, - } = useQuery({ + } = useSuspenseQuery({ queryKey: getAvailableArtworksQuery().queryKey, queryFn: getAvailableArtworksQuery().queryFn, staleTime: 1000 * 60 * 60 * 2, // 2시간 gcTime: 1000 * 60 * 60 * 1.5, // 1.5시간 }); if (error) { - console.error(error); + handleError(error); } return { availableArtworks, diff --git a/src/pages/auction/hooks/useAuctionLike.ts b/src/pages/auction/hooks/useAuctionLike.ts index 406b592..1ec54e9 100644 --- a/src/pages/auction/hooks/useAuctionLike.ts +++ b/src/pages/auction/hooks/useAuctionLike.ts @@ -17,11 +17,11 @@ export const useAuctionLike = () => { onSuccess: () => { toast.success(`좋아요 변경 성공`); queryClient.invalidateQueries({ - queryKey: ['auctionDetails'], + queryKey: postAuctionLikeMutation().successMutationKey, }); }, - onError: () => { - toast.error('좋아요를 변경하는 데 실패했습니다.'); + onError: (error) => { + toast.error(`좋아요 변경에 실패했습니다. ${error.message}`); }, }); diff --git a/src/pages/auction/hooks/useAuctionUnlike.ts b/src/pages/auction/hooks/useAuctionUnlike.ts index 10cee5a..4382b55 100644 --- a/src/pages/auction/hooks/useAuctionUnlike.ts +++ b/src/pages/auction/hooks/useAuctionUnlike.ts @@ -6,7 +6,7 @@ import { postAuctionUnlike } from '@/apis/auction/postAuctionUnlike'; /** * 경매 좋아요 취소 커스텀 훅 * @returns null - * @author 이하늘 + * @author 이하늘, 홍규진 */ export const useAuctionUnlike = () => { const queryClient = useQueryClient(); @@ -17,11 +17,11 @@ export const useAuctionUnlike = () => { onSuccess: () => { toast.success(`좋아요 변경 성공`); queryClient.invalidateQueries({ - queryKey: ['auctionDetails'], + queryKey: postAuctionUnlikeMutation().successMutationKey, }); }, - onError: () => { - toast.error('좋아요를 변경하는 데 실패했습니다.'); + onError: (error) => { + toast.error(`좋아요 변경에 실패했습니다. ${error.message}`); }, }); diff --git a/src/pages/auction/hooks/useGetAuctionLists.ts b/src/pages/auction/hooks/useGetAuctionLists.ts index 0496558..f12b207 100644 --- a/src/pages/auction/hooks/useGetAuctionLists.ts +++ b/src/pages/auction/hooks/useGetAuctionLists.ts @@ -1,9 +1,7 @@ import { getAuctionListQuery } from '@/constants/queryKeys'; import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; import { TGetAuctionListResponse } from '@/apis/auction/type'; - +import { handleError } from '@/utils/handleError'; /** * 경매 리스트를 가져오는 커스텀 훅 * React Query의 useSuspenseQuery를 활용해 데이터를 가져오고, 오류 발생 시 toast 알림을 띄움 @@ -11,7 +9,7 @@ import { TGetAuctionListResponse } from '@/apis/auction/type'; * @param { 'title' | 'popular' | 'latest' } sort - 정렬 기준 * @returns {object} data, error 상태 반환 * @example const { data } = useGetAuctionLists('title'); - * @author 이하늘 + * @author 이하늘, 홍규진 **/ export const useGetAuctionLists = (sort: string) => { const { data, isLoading, error } = useSuspenseQuery< @@ -24,11 +22,7 @@ export const useGetAuctionLists = (sort: string) => { }); if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '경매 목록을 불러오는데 실패했습니다.'; - toast.error(errorMessage); + handleError(error); } return { data, isLoading, error }; diff --git a/src/pages/author-detail/hooks/useGetAuthorDetail.ts b/src/pages/author-detail/hooks/useGetAuthorDetail.ts index e2a40e2..a6b8d79 100644 --- a/src/pages/author-detail/hooks/useGetAuthorDetail.ts +++ b/src/pages/author-detail/hooks/useGetAuthorDetail.ts @@ -1,7 +1,6 @@ import { getAuthorDetailQuery } from '@/constants/queryKeys'; import { useSuspenseQuery } from '@tanstack/react-query'; import { toast } from 'sonner'; -import { AxiosError } from 'axios'; import { TGetAuthorDetailApiResponse } from '@/apis/author/type'; /** @@ -15,7 +14,7 @@ import { TGetAuthorDetailApiResponse } from '@/apis/author/type'; * @param {number} exhibitionLimit - 전시 한 페이지에 표시할 개수 (기본값: 4) * @returns {object} data, isLoading, error 상태 반환 * @example const { data } = useGetAuthorDetail(1); - * @author 이하늘 + * @author 이하늘, 홍규진 **/ export const useGetAuthorDetail = ({ authorId, @@ -50,11 +49,7 @@ export const useGetAuthorDetail = ({ gcTime: 1000 * 60 * 30, // 30분 }); if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '작가 정보를 불러오는데 실패했습니다.'; - toast.error(errorMessage); + toast.error(`작가 상세 정보 조회에 실패했습니다. ${error.message}`); } return { data, isLoading, error }; diff --git a/src/pages/author/hooks/useGetAuthorLists.ts b/src/pages/author/hooks/useGetAuthorLists.ts index 10692e2..851aed0 100644 --- a/src/pages/author/hooks/useGetAuthorLists.ts +++ b/src/pages/author/hooks/useGetAuthorLists.ts @@ -1,9 +1,7 @@ import { getAuthorListQuery } from '@/constants/queryKeys'; import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; import { TGetAuthorListApiResponse } from '@/apis/author/type'; - +import { handleError } from '@/utils/handleError'; /** * 작가 리스트를 가져오는 커스텀 훅 * React Query의 useSuspenseQuery를 활용해 데이터를 가져오고, 오류 발생 시 toast 알림을 띄움 @@ -13,7 +11,7 @@ import { TGetAuthorListApiResponse } from '@/apis/author/type'; * @param {number} limit - 페이지당 항목 수 * @returns {object} data, isLoading, error 상태 반환 * @example const { data } = useGetAuthorLists('popularity', 1, 5); - * @author 이하늘 + * @author 이하늘, 홍규진 **/ export const useGetAuthorLists = ( sort: string, @@ -29,11 +27,7 @@ export const useGetAuthorLists = ( }); if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '작가 목록을 불러오는데 실패했습니다.'; - toast.error(errorMessage); + handleError(error); } return { data, isLoading, error }; diff --git a/src/pages/exhibition/hooks/useGetExhibitionList.ts b/src/pages/exhibition/hooks/useGetExhibitionList.ts index 95323e4..56d7aa1 100644 --- a/src/pages/exhibition/hooks/useGetExhibitionList.ts +++ b/src/pages/exhibition/hooks/useGetExhibitionList.ts @@ -1,8 +1,7 @@ import { getExhibitionListQuery } from '@/constants/queryKeys'; import { useSuspenseQuery } from '@tanstack/react-query'; -import { toast } from 'sonner'; -import { AxiosError } from 'axios'; import { TExhibition } from '@/apis/exhibition/type'; +import { handleError } from '@/utils/handleError'; /** * 전시 리스트를 가져오는 커스텀 훅 @@ -11,7 +10,7 @@ import { TExhibition } from '@/apis/exhibition/type'; * @param { 'title' | 'popular' | 'latest' } sort - 정렬 기준 * @returns {object} data, error 상태 반환 * @example const { data } = useGetAuctionLists('title'); - * @author 이하늘 + * @author 이하늘, 홍규진 **/ export const useGetExhibitionList = (sort: string) => { const { data, isLoading, error } = useSuspenseQuery({ @@ -22,11 +21,7 @@ export const useGetExhibitionList = (sort: string) => { }); if (error) { - const axiosError = error as AxiosError<{ message?: string }>; - const errorMessage = - axiosError.response?.data?.message || - '전시 목록을 불러오는데 실패했습니다.'; - toast.error(errorMessage); + handleError(error); } return { data, isLoading, error }; diff --git a/src/pages/login/hooks/useOauthLogin.ts b/src/pages/login/hooks/useOauthLogin.ts index 5d123aa..9b8afc5 100644 --- a/src/pages/login/hooks/useOauthLogin.ts +++ b/src/pages/login/hooks/useOauthLogin.ts @@ -1,10 +1,12 @@ import { postOauthLoginWithCode } from '@/apis/register/oAuthLogin'; import { useMutation } from '@tanstack/react-query'; +import { handleError } from '@/utils/handleError'; import { toast } from 'sonner'; /** * 소셜 로그인 뮤테이션 훅입니다. * 인가 코드를 사용하여 소셜 로그인 요청을 처리합니다. * @returns {object} mutate, isLoading, isError 상태 반환 + * @author 홍규진 */ export const useOauthLoginMutation = (code: string, social_type: string) => { const mutation = useMutation({ @@ -23,8 +25,7 @@ export const useOauthLoginMutation = (code: string, social_type: string) => { }, onError: (error: Error) => { // 로그인 실패 시 처리 - console.error('로그인 실패:', error); - toast.error(`로그인 실패: ${error.message}`); + handleError(error); }, }); diff --git a/src/pages/main/hooks/useGetMainData.ts b/src/pages/main/hooks/useGetMainData.ts index 07e92c9..c9fe5a8 100644 --- a/src/pages/main/hooks/useGetMainData.ts +++ b/src/pages/main/hooks/useGetMainData.ts @@ -1,14 +1,13 @@ import { useSuspenseQuery } from '@tanstack/react-query'; -import { getMainData } from '@/apis/main/main'; import { getMainDataQuery } from '@/constants/queryKeys'; import { TMainResult } from '@/apis/main/type'; - +import { handleError } from '@/utils/handleError'; /** * 메인 페이지 데이터를 가져오는 커스텀 훅 * React Query의 useSuspenseQuery를 활용해 데이터를 가져옵니다. * 오류 발생 시 toast 알림을 통해 사용자에게 에러 메시지를 표시합니다. * @returns {object} mainData, isLoading, error 상태 반환 - * @author 김서윤 + * @author 김서윤, 홍규진 **/ export const useGetMainData = () => { const { @@ -17,10 +16,14 @@ export const useGetMainData = () => { error, } = useSuspenseQuery({ queryKey: getMainDataQuery().queryKey, - queryFn: getMainData, + queryFn: getMainDataQuery().queryFn, staleTime: 1000 * 60 * 60, // 1시간 gcTime: 1000 * 60 * 30, // 30분 }); + if (error) { + handleError(error); + } + return { mainData, isLoading, error }; }; diff --git a/src/pages/register/hooks/usePostAuthRegister.ts b/src/pages/register/hooks/usePostAuthRegister.ts index b5f6167..68349eb 100644 --- a/src/pages/register/hooks/usePostAuthRegister.ts +++ b/src/pages/register/hooks/usePostAuthRegister.ts @@ -21,7 +21,7 @@ export const usePostAuthRegister = () => { navigate('/'); // 성공 페이지로 이동 }, onError: (error: Error) => { - toast.error(error.message); // 오류 메시지 표시 + toast.error(`회원가입에 실패했습니다. ${error.message}`); }, }); }; From bbb36342d189649934c0a1855924f57c4ef1698f Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:04:30 +0900 Subject: [PATCH 10/11] =?UTF-8?q?Refactor:=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=97=A8=EB=93=A4=EB=A7=81=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/handleError.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/utils/handleError.ts diff --git a/src/utils/handleError.ts b/src/utils/handleError.ts new file mode 100644 index 0000000..ccf5390 --- /dev/null +++ b/src/utils/handleError.ts @@ -0,0 +1,15 @@ +import { toast } from 'sonner'; + +/** + * 페이지 전체를 표시하는 API 호출중 에러가 발생할 경우 사용 or 치명적인 에러 발생시 사용 + * 예시) 메인 페이지 데이터 조회 중 에러가 발생할 경우 + * ErrorBoundary 컴포넌트에서 DefaultFallBack UI 사용 + * @param error 에러 객체 + * @author 홍규진 + */ +export const handleError = (error: Error) => { + const errorMessage = error.message; + console.log(errorMessage); + toast.error(errorMessage); + throw new Error(errorMessage); +}; From 82fd83411672ae269c5cdd780a04f4183948e0f8 Mon Sep 17 00:00:00 2001 From: kyujenius Date: Mon, 17 Feb 2025 20:04:54 +0900 Subject: [PATCH 11/11] =?UTF-8?q?Refactor:=20=EB=9D=BC=EC=9A=B0=ED=8C=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/router.tsx b/src/router.tsx index 79c2b37..d1dc915 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -2,8 +2,8 @@ import { Route, Routes } from 'react-router-dom'; import { AuthCheckRoute } from '@components/common/AuthCheckRoute'; import NotFound from './pages/not-found'; import Test from '@pages/test.tsx'; -import ArtBuyerPage from './pages/artBuyerPage/ArtBuyerPage'; -import AuthorPage from './pages/authorPage/AuthorPage'; +import { MypageBuyerPage } from './pages/mypage-buyer-page'; +import { MypageAuthorPage } from './pages/mypage-author-page'; import { Login } from '@/pages/login'; import { ArtworkRegister } from '@/pages/artwork-register'; @@ -13,7 +13,7 @@ import { AuthorDetail } from './pages/author-detail'; import { Exhibition } from './pages/exhibition'; import { ExhibitionDetail } from './pages/exhibition-detail'; import { Register } from '@/pages/register'; -import { ExhibitRegister } from '@/pages/exhibit-register'; +import { ExhibitionRegister } from '@/pages/exhibition-register'; import { Auction } from './pages/auction'; import { Main } from '@/pages/main'; import { ArtWork } from '@/pages/artwork'; @@ -32,16 +32,16 @@ type TRoutes = { * */ /** - * 작품구매자_마이페이지는 /artBuyerPage 이고, - * 작가_마이페이지는 /authorPage 로 구분했습니다. - * @author 노찬영 + * 작품구매자_마이페이지는 /mypage/buyer 이고, + * 작가_마이페이지는 /mypage/author 로 구분했습니다. + * @author 노찬영, 홍규진 * */ // eslint-disable-next-line react-refresh/only-export-components export const routes: TRoutes[] = [ { path: '/', element:
, isTabBar: true }, - { path: '/mypage/art-buyer', element: , isTabBar: true }, - { path: '/mypage/author', element: , isTabBar: true }, + { path: '/mypage/buyer', element: , isTabBar: true }, + { path: '/mypage/author', element: , isTabBar: true }, { path: '/test', element: , isTabBar: true }, { path: '/login', element: , isTabBar: true }, @@ -79,8 +79,8 @@ export const routes: TRoutes[] = [ isTabBar: true, }, { - path: '/exhibit-register', - element: , + path: '/exhibition-register', + element: , isTabBar: true, }, {