diff --git a/README.md b/README.md index b914539..04f1297 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,8 @@ - 전시는 여러 사용자들이 관람 뿐 아니라 채팅을 통해 서로 의사소통 가능합니다. - 전시 작가는 다양한 템플릿으로 전시를 할 수 있습니다. - 오프라인 뿐 아니라 온라인에서도 홍보를 하고싶어 할 경우에 유용한 플랫폼입니다. -
- -**프로젝트 기간** : 2024.05.22 ~ 2024.07.11 +
-🔗 [D'art 서비스 둘러보기](https://www.dartgallery.site/) (데스크탑/태블릿 환경에서 이용 가능) - -🎬 [시연 영상 보러가기 ](--)

@@ -66,7 +61,8 @@ ### 📌 실시간 채팅 서비스 -- 전시를 감상하며 나와 같이 보고 있는 사람들과 얘기를 나눌 수도, 큐레이터의 안내를 받을 수도 있습니다. +- 전시를 감상하며 현재 해당 전시를 관람하고 있는 사람을 확인할 수 있습니다. +- 관람객과 얘기를 나눌 수도, 작가의 안내를 받을 수도 있습니다. 스크린샷 2024-07-10 오후 4 21 58 ### 📌 리뷰 및 공유하기 @@ -118,11 +114,11 @@ ## 🧑‍🧑‍🧒‍🧒 Front TEAMMATE 소개 -| ![강신범](https://github.com/kangsinbeom.png) | ![김수현](https://github.com/gamjatan9.png) | ![하승진](https://github.com/j2an777.png) | -| --------------------------------------------- | ------------------------------------------- | ----------------------------------------- | --- | -| [강신범](https://github.com/kangsinbeom) | [김수현](https://github.com/gamjatan9) | [하승진](https://github.com/j2an777) | -| **FrontEnd** | **FrontEnd** | **FrontEnd** | | -| FE 팀장, 에러처리, 데이터와 토큰 관리, 배포, 메인 페이지, 공통 컴포넌트와 훅 생성 | 폼 관리, 실시간 채팅, 결제 시스템, 전시 생성 페이지 | 전시 페이지, 모달 기능, 저작권 침해 방지 마련, 전시 생성 ux 강화 | +| ![강신범](https://github.com/kangsinbeom.png) | ![김수현](https://github.com/gamjatan9.png) | ![하승진](https://github.com/j2an777.png) | +| --------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------- | +| [강신범](https://github.comzkangsinbeom) | [김수현](https://github.com/gamjatan9) | [하승진](https://github.com/j2an777) | +| **FrontEnd** | **FrontEnd** | **FrontEnd** | +| FE 팀장, 에러처리, 데이터와 토큰 관리, 배포, 메인 페이지, 공통 컴포넌트와 훅 생성 | 폼 관리, 실시간 채팅, 결제 시스템, 전시 생성 페이지, 회원정보조회 | 전시 페이지, 모달 기능, 저작권 침해 방지 마련, 전시 생성 ux 강화 |
diff --git a/src/consts/postValidation.ts b/src/consts/postValidation.ts new file mode 100644 index 0000000..4d06b66 --- /dev/null +++ b/src/consts/postValidation.ts @@ -0,0 +1,17 @@ +import { PostGalleries } from '@/types/post'; + +export const postValidation = (data: PostGalleries) => { + if (!data.images || data.images.length < 3) { + throw new Error('최소 3개의 작품을 등록해주세요.'); + } + + if (!data.thumbnail) { + throw new Error('썸네일 이미지가 없습니다.'); + } + + if (data.gallery.fee && !data.gallery.endDate) { + throw new Error('유료 전시 기간을 선택해 주세요.'); + } + + return true; +}; diff --git a/src/pages/chatModal/components/chat/ChatMenu.tsx b/src/pages/chatModal/components/chat/ChatMenu.tsx index e6dcedf..a2977d7 100644 --- a/src/pages/chatModal/components/chat/ChatMenu.tsx +++ b/src/pages/chatModal/components/chat/ChatMenu.tsx @@ -9,6 +9,7 @@ import { useChatScroll } from '../../hooks/useChatScroll'; import { ChatProps } from '../..'; import * as S from './styles'; import parseDate from '@/utils/parseDate'; +import { formatDate } from '@/utils/formatDateforChat'; const ChatMenu = ({ chatRoomId, galleryNick }: Omit) => { const [messages, setMessages] = useState([]); @@ -60,7 +61,7 @@ const ChatMenu = ({ chatRoomId, galleryNick }: Omit) => { const message: ChatMessageProps = { content: newMessage, sender: nickname, - createdAt: new Date(), + createdAt: formatDate(new Date()), isAuthor: isAuthor, profileImageUrl: profileImage, }; diff --git a/src/pages/payment/success/index.tsx b/src/pages/payment/success/index.tsx index bd2ba48..b7fb9c5 100644 --- a/src/pages/payment/success/index.tsx +++ b/src/pages/payment/success/index.tsx @@ -10,7 +10,7 @@ const SuccessPage = () => { switch (order) { case 'ticket': - title = '결재가 완료되었습니다.'; + title = '결제가 완료되었습니다.'; content = '전시관 입장하기'; path = `/gallery/${galleryId}`; break; diff --git a/src/pages/post/components/stepZero/index.tsx b/src/pages/post/components/stepZero/index.tsx index 3d22aae..7051b4a 100644 --- a/src/pages/post/components/stepZero/index.tsx +++ b/src/pages/post/components/stepZero/index.tsx @@ -1,13 +1,13 @@ import { useEffect } from 'react'; -import { useFormContext } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import DropZone from '@/components/dropZone'; -import { formatDateInKST } from '@/utils/formatDateInKST'; +import { formatDate } from '@/utils/formatDateforPost'; import * as S from './styles'; import { dateNfeeStore } from '@/stores/post'; import { Icon } from '@/components'; const StepZero = () => { - const { setValue } = useFormContext(); + const { setValue, control } = useFormContext(); const { activeBtn, dateRange, feeDetails, setActiveBtn, setDateRange, setFeeDetails } = dateNfeeStore(); @@ -41,22 +41,18 @@ const StepZero = () => { setFeeDetails({ ...feeDetails, totalPay: pay }); setDateRange({ ...dateRange, endDate }); // 이용료와 기간 formData 업데이트 - setValue('gallery.startDate', formatDateInKST(dateRange.startDate, true)); - setValue('gallery.endDate', formatDateInKST(endDate)); + setValue('gallery.startDate', formatDate(dateRange.startDate, true)); + setValue('gallery.endDate', formatDate(endDate)); setValue('gallery.generatedCost', pay); } else { // 무료 전시 setValue('gallery.generatedCost', 0); - setValue('gallery.startDate', formatDateInKST(dateRange.startDate, true)); + setValue('gallery.startDate', formatDate(dateRange.startDate, true)); setFeeDetails({ ...feeDetails, totalPay: 0 }); setDateRange({ ...dateRange, endDate: null }); } }, [activeBtn, feeDetails.period, feeDetails.fee, dateRange.startDate]); - const onFileDrop = (file: File) => { - setValue('thumbnail', file); - }; - const onBtnClick = ( buttonType: 'free' | 'pay', event: React.MouseEvent, @@ -109,9 +105,15 @@ const StepZero = () => { return ( - ( + field.onChange(file)} + /> + )} /> diff --git a/src/pages/post/hooks/useHandleErrors.ts b/src/pages/post/hooks/useHandleErrors.ts index d592d5c..ce07cca 100644 --- a/src/pages/post/hooks/useHandleErrors.ts +++ b/src/pages/post/hooks/useHandleErrors.ts @@ -1,33 +1,24 @@ import { alertStore } from '@/stores/modal'; -import useCustomNavigate from '@/hooks/useCustomNavigate'; import axios from 'axios'; -import { PostGalleries } from '@/types/post'; export const useHandleErrors = () => { const open = alertStore((state) => state.open); - const navigate = useCustomNavigate(); - const handleErrors = (error: unknown, data: PostGalleries) => { - if (axios.isAxiosError(error) && error.response?.status === 401) { - open({ - title: '재로그인 필요', - description: '로그인 시간이 만료되었습니다. 다시 로그인해 주세요.', - buttonLabel: '확인', - onClickButton: () => navigate('/login'), - }); - } else if (data.images == undefined || data.images.length < 3) { - open({ - title: '작품 등록 오류', - description: '최소 3개의 작품을 등록해주세요.', - buttonLabel: '확인', - }); - } else { - open({ - title: '전시 등록 오류', - description: '모든 정보를 입력해 주세요.', - buttonLabel: '확인', - }); + const handleErrors = (error: unknown) => { + let message; + + if (axios.isAxiosError(error)) { + message = error.response?.data.message || message; + } else if (error instanceof Error) { + message = error.message; } + + open({ + title: '작품 등록 오류', + description: message, + buttonLabel: '확인', + onClickButton: () => {}, + }); }; return { handleErrors }; diff --git a/src/pages/post/index.tsx b/src/pages/post/index.tsx index 4413f64..7f48a85 100644 --- a/src/pages/post/index.tsx +++ b/src/pages/post/index.tsx @@ -6,6 +6,8 @@ import { alertStore, progressStore } from '@/stores/modal'; import useCustomNavigate from '@/hooks/useCustomNavigate'; import usePostGalleries from './hooks/usePostGalleries'; import ProgressPortal from '@/components/ProgressPortal'; +import { postValidation } from '@/consts/postValidation'; +import { useHandleErrors } from './hooks/useHandleErrors'; import * as S from './styles'; @@ -15,8 +17,8 @@ const PostPage = () => { const navigate = useCustomNavigate(); const { open } = alertStore(); const setProgress = progressStore((state) => state.setProgress); - const { mutate } = usePostGalleries(); + const { handleErrors } = useHandleErrors(); const onSubmit: SubmitHandler = async (data) => { open({ @@ -33,22 +35,23 @@ const PostPage = () => { ), buttonLabel: '확인', buttonCancelLabel: '취소', - onClickButton: () => modalConfirm(data), + onClickButton: () => setTimeout(() => modalConfirm(data), 100), onClickCancelButton: () => {}, }); }; const modalConfirm = async (data: PostGalleries) => { - if (data.images == undefined || data.images.length < 3) { - open({ - title: '작품 등록 오류', - description: '최소 3개의 작품을 등록해주세요.', - buttonLabel: '확인', + try { + postValidation(data); + setProgress(1); + mutate(data, { + onError: (error) => { + handleErrors(error); + }, }); - return; + } catch (error) { + handleErrors(error); } - setProgress(1); - mutate(data); }; return ( diff --git a/src/types/chat.ts b/src/types/chat.ts index 5b99576..5ddc058 100644 --- a/src/types/chat.ts +++ b/src/types/chat.ts @@ -11,7 +11,7 @@ export interface ChatMessage { export interface ChatMessageProps { sender: string; content: string; - createdAt: Date; + createdAt: Date | string; isAuthor: boolean; profileImageUrl: string; } diff --git a/src/utils/formatDateforChat.ts b/src/utils/formatDateforChat.ts new file mode 100644 index 0000000..60df017 --- /dev/null +++ b/src/utils/formatDateforChat.ts @@ -0,0 +1,11 @@ +export const formatDate = (date: Date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + const formattedDateString = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`; + return formattedDateString; +}; diff --git a/src/utils/formatDateInKST.ts b/src/utils/formatDateforPost.ts similarity index 79% rename from src/utils/formatDateInKST.ts rename to src/utils/formatDateforPost.ts index 9d9afb3..a1a51c1 100644 --- a/src/utils/formatDateInKST.ts +++ b/src/utils/formatDateforPost.ts @@ -1,4 +1,4 @@ -export const formatDateInKST = (date: Date | null, startDate: boolean = false) => { +export const formatDate = (date: Date | null, startDate: boolean = false) => { if (!date) return null; const year = date.getFullYear(); diff --git a/src/utils/parseDate.ts b/src/utils/parseDate.ts index 544ce73..cf35c0b 100644 --- a/src/utils/parseDate.ts +++ b/src/utils/parseDate.ts @@ -1,4 +1,4 @@ -const parseDate = (value: Date) => { +const parseDate = (value: Date | string) => { const date = new Date(value); const year = date.getFullYear(); const month = date.getMonth() + 1;