diff --git a/src/api/joinProject.api.ts b/src/api/joinProject.api.ts index 10d814de..c2d87a3d 100644 --- a/src/api/joinProject.api.ts +++ b/src/api/joinProject.api.ts @@ -10,7 +10,7 @@ export const getProjectData = async ( return response.data; }; -export const createProject = async (formData: FormData) => { +export const postProject = async (formData: FormData) => { const response = await httpClient.post(`/project`, formData); return response.status; }; diff --git a/src/components/applyComponents/careersComponent/CareersComponent.styled.ts b/src/components/applyComponents/careersComponent/CareersComponent.styled.ts index fda6185e..840242e4 100644 --- a/src/components/applyComponents/careersComponent/CareersComponent.styled.ts +++ b/src/components/applyComponents/careersComponent/CareersComponent.styled.ts @@ -11,7 +11,7 @@ export const CareerContainer = styled.div` width: 100%; margin-bottom: 10px; - @media (max-width: 963px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { flex-direction: column; gap: 10px; } diff --git a/src/components/applyComponents/careersComponent/careersInputComponent/CareersComponentInput.styled.ts b/src/components/applyComponents/careersComponent/careersInputComponent/CareersComponentInput.styled.ts index 1027f91b..e35ac694 100644 --- a/src/components/applyComponents/careersComponent/careersInputComponent/CareersComponentInput.styled.ts +++ b/src/components/applyComponents/careersComponent/careersInputComponent/CareersComponentInput.styled.ts @@ -13,7 +13,7 @@ const dateStyle = css` export const CareerInput = styled.input` ${basicStyle} padding: 10px; - font-size: 18px; + font-size: 16px; &:focus { outline: none; @@ -40,18 +40,19 @@ export const CareerInput = styled.input` font-size: ${({ theme }) => theme.heading.small.fontSize}; } - @media (max-width: 963px) { - width: 100%; + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { flex: 1; margin-bottom: 10px; &:nth-child(1), &:nth-child(4) { + width: 60%; flex: 1; } &:nth-child(2), &:nth-child(3) { + width: 20%; flex: 1; } } diff --git a/src/components/applyComponents/phoneComponent/PhoneComponent.styled.ts b/src/components/applyComponents/phoneComponent/PhoneComponent.styled.ts index 9d1be0e5..52b0919b 100644 --- a/src/components/applyComponents/phoneComponent/PhoneComponent.styled.ts +++ b/src/components/applyComponents/phoneComponent/PhoneComponent.styled.ts @@ -8,20 +8,6 @@ export const PhoneInputContainer = styled.div` position: relative; `; -export const PhoneInput = styled.input<{ name: string }>` - width: 60px; - padding: 10px; - border: 1px solid ${({ theme }) => theme.color.border}; - border-radius: ${({ theme }) => theme.borderRadius.primary}; - text-align: center; - font-size: 18px; - - &:focus { - outline: none; - border-color: #888; - } -`; - export const Dash = styled.span` align-self: center; font-size: 25px; @@ -30,7 +16,7 @@ export const Dash = styled.span` export const FormError = styled.p` margin-top: 0.3px; - font-size: 1rem; + font-size: 0.9rem; color: ${({ theme }) => theme.color.red}; position: absolute; top: 115%; diff --git a/src/components/applyComponents/phoneComponent/phoneComponentInput/PhoneComponentInput.styled.ts b/src/components/applyComponents/phoneComponent/phoneComponentInput/PhoneComponentInput.styled.ts index 4763c3dc..d0e56279 100644 --- a/src/components/applyComponents/phoneComponent/phoneComponentInput/PhoneComponentInput.styled.ts +++ b/src/components/applyComponents/phoneComponent/phoneComponentInput/PhoneComponentInput.styled.ts @@ -6,20 +6,9 @@ export const PhoneInput = styled.input` border: 1px solid ${({ theme }) => theme.color.border}; border-radius: ${({ theme }) => theme.borderRadius.primary}; text-align: center; - font-size: 19px; + font-size: 17px; - &:focus { - outline: none; - border-color: #888; + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + font-size: 15px; } `; - -export const FormError = styled.p` - margin-top: 0.3px; - font-size: 1rem; - color: ${({ theme }) => theme.color.red}; - position: absolute; - top: 100%; - left: 0; - white-space: nowrap; -`; diff --git a/src/components/common/modal/Modal.tsx b/src/components/common/modal/Modal.tsx index eb3b086d..e5191906 100644 --- a/src/components/common/modal/Modal.tsx +++ b/src/components/common/modal/Modal.tsx @@ -1,8 +1,10 @@ -import { useRef, useState } from 'react'; +import { useState } from 'react'; import { createPortal } from 'react-dom'; import { XMarkIcon, CheckCircleIcon } from '@heroicons/react/24/outline'; import * as S from './Modal.styled'; import ScrollPreventor from './ScrollPreventor'; +import { useOutsideClick } from '../../../hooks/useOutsideClick'; + interface ModalProps { children: React.ReactNode; isOpen: boolean; @@ -10,19 +12,13 @@ interface ModalProps { } const Modal = ({ children, isOpen, onClose }: ModalProps) => { - const modalRef = useRef(null); + const modalRefs = useOutsideClick(() => handleClose()); const [isFadingOut, setIsFadingOut] = useState(false); const handleClose = () => { setIsFadingOut(true); }; - const handleOverlayClick = (e: React.MouseEvent) => { - if (modalRef.current && !modalRef.current.contains(e.target as Node)) { - handleClose(); - } - }; - const handleAnimationEnd = () => { if (isFadingOut) { onClose(); @@ -36,10 +32,9 @@ const Modal = ({ children, isOpen, onClose }: ModalProps) => { - + diff --git a/src/components/projectFormComponents/inputComponent/inputComponent.styled.ts b/src/components/projectFormComponents/inputComponent/inputComponent.styled.ts index b3681717..d052beab 100644 --- a/src/components/projectFormComponents/inputComponent/inputComponent.styled.ts +++ b/src/components/projectFormComponents/inputComponent/inputComponent.styled.ts @@ -10,7 +10,7 @@ export const InputStyle = styled.input<{ type?: string }>` padding: 10px; border: 1px solid ${({ theme }) => theme.color.border}; border-radius: ${({ theme }) => theme.borderRadius.primary}; - font-size: ${({ theme }) => theme.heading.semiSmall.fontSize}; + font-size: ${({ theme }) => theme.heading.small.fontSize}; ${({ type }) => { switch (type) { @@ -24,7 +24,6 @@ export const InputStyle = styled.input<{ type?: string }>` flex: 0.1; background-color: #ffffff; color: #aaa; - font-family: 'Arial', sans-serif; &::placeholder { color: #aaa; @@ -74,7 +73,7 @@ export const InputInfoStyle = styled.input<{ type?: string }>` export const FormError = styled.p` margin-top: 0.3px; - font-size: 1rem; + font-size: 0.9rem; color: ${({ theme }) => theme.color.red}; position: absolute; top: 115%; diff --git a/src/components/projectFormComponents/projectInformationText/ProjectInformation.styled.ts b/src/components/projectFormComponents/projectInformationText/ProjectInformation.styled.ts index 8a8a763b..8b3b7fde 100644 --- a/src/components/projectFormComponents/projectInformationText/ProjectInformation.styled.ts +++ b/src/components/projectFormComponents/projectInformationText/ProjectInformation.styled.ts @@ -6,7 +6,7 @@ export const SectionInput = styled.div` flex-direction: column; gap: 1px; - @media (max-width: 1024px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { margin-bottom: 25px; } `; @@ -25,9 +25,9 @@ export const InfoRow = styled.div` margin-right: 10px; } - @media (max-width: 1024px) { - margin-bottom: 15px; - } + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + align-items: flex-start; + margin-bottom: 15px; `; export const InfoLabel = styled.label` @@ -39,7 +39,7 @@ export const InfoLabel = styled.label` margin-right: 15px; white-space: nowrap; - @media (max-width: 1024px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { margin-right: 30px; margin-bottom: 8px; font-size: 0.9rem; @@ -53,7 +53,7 @@ export const InfoText = styled.p` flex: 0.8; text-align: left; - @media (max-width: 1024px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { font-size: 0.95rem; flex: 1; } @@ -65,7 +65,7 @@ export const SkillTagContainer = styled.div` gap: 10px; flex: 0.9; - @media (max-width: 1024px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { grid-template-columns: repeat(6, 1fr); gap: 8px; width: 100%; @@ -94,7 +94,7 @@ export const SkillTagImage = styled.div` margin-top: 2px; } - @media (max-width: 1024px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { img { width: 35px; height: 35px; @@ -116,7 +116,7 @@ export const BeginnerIcon = styled.img` object-fit: contain; margin-bottom: 15px; - @media (max-width: 1024px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { width: 18px; height: 18px; margin-bottom: 10px; diff --git a/src/constants/modalMessage.ts b/src/constants/modalMessage.ts index 36a8466e..fa62db29 100644 --- a/src/constants/modalMessage.ts +++ b/src/constants/modalMessage.ts @@ -16,4 +16,10 @@ export const MODAL_MESSAGE = { myProfileFail: '프로필 수정에 실패했습니다.', profileImgSuccess: '프로필 이미지가 업로드 되었습니다.', profileImgFail: '이미지는 5MB 이하, .png.jpg.jpeg.svg 형식만 가능합니다.', + ModifyProjectSuccess: '공고 수정이 정상적으로 완료 되었습니다.', + ModifyProjectFail: '공고 수정을 실패 했습니다.', + createProjectSuccess: '공고 생성을 완료 했습니다.', + createProjectFail: '공고 생성을 실패 했습니다.', + applyProjectSuccess: '해당 공고에 지원을 완료 되었습니다.', + applyProjectFail: '해당 공고에 지원을 실패 되었습니다.', } as const; diff --git a/src/hooks/useApplyProject.ts b/src/hooks/useApplyProject.ts new file mode 100644 index 00000000..2b119eae --- /dev/null +++ b/src/hooks/useApplyProject.ts @@ -0,0 +1,44 @@ +import { useMutation } from '@tanstack/react-query'; +import { MODAL_MESSAGE } from '../constants/modalMessage'; +import { postApplicantProject } from '../api/joinProject.api'; +import { ROUTES } from '../constants/routes'; +import { useNavigate } from 'react-router-dom'; +import { joinProject } from '../models/joinProject'; + +interface UseApplyProjectProps { + id: number; + handleModalOpen: (newMessage: string) => void; +} + +const useApplyProject = ({ id, handleModalOpen }: UseApplyProjectProps) => { + const navigate = useNavigate(); + + const mutation = useMutation({ + mutationFn: (formData: joinProject) => postApplicantProject(formData, id), + onSuccess: () => { + handleModalOpen(MODAL_MESSAGE.applyProjectSuccess); + + setTimeout(() => { + navigate(ROUTES.main); + }, 3000); + }, + onError: (error) => { + console.log(error); + handleModalOpen(MODAL_MESSAGE.applyProjectFail); + }, + }); + + const applyProject = async (formData: joinProject) => { + mutation.mutate(formData); + }; + + return { + applyProject, + isLoading: mutation.isPending, + isError: mutation.isError, + error: mutation.error, + isSuccess: mutation.isSuccess, + }; +}; + +export default useApplyProject; diff --git a/src/hooks/useCreateProject.ts b/src/hooks/useCreateProject.ts new file mode 100644 index 00000000..be5fd339 --- /dev/null +++ b/src/hooks/useCreateProject.ts @@ -0,0 +1,52 @@ +import { useMutation } from '@tanstack/react-query'; +import { FormData } from '../models/createProject'; +import { MODAL_MESSAGE } from '../constants/modalMessage'; +import { postProject } from '../api/joinProject.api'; +import { Dispatch, SetStateAction } from 'react'; +import { useSaveSearchFiltering } from './useSaveSearchFiltering'; +import { ROUTES } from '../constants/routes'; +import { useNavigate } from 'react-router-dom'; + +interface UseCreateProjectProps { + handleModalOpen: (newMessage: string) => void; + setIsSubmit: Dispatch>; +} + +const useCreateProject = ({ + handleModalOpen, + setIsSubmit, +}: UseCreateProjectProps) => { + const navigate = useNavigate(); + const { handleUpdateFilters } = useSaveSearchFiltering(); + + const mutation = useMutation({ + mutationFn: (formData: FormData) => postProject(formData), + onSuccess: () => { + handleModalOpen(MODAL_MESSAGE.createProjectSuccess); + setIsSubmit(true); + handleUpdateFilters('skillTag', []); + + setTimeout(() => { + navigate(ROUTES.main); + }, 3000); + }, + onError: (error) => { + console.log(error); + handleModalOpen(MODAL_MESSAGE.createProjectFail); + }, + }); + + const createProject = async (formData: FormData) => { + mutation.mutate(formData); + }; + + return { + createProject, + isLoading: mutation.isPending, + isError: mutation.isError, + error: mutation.error, + isSuccess: mutation.isSuccess, + }; +}; + +export default useCreateProject; diff --git a/src/hooks/useModal.ts b/src/hooks/useModal.ts index 0d15a2d4..62cf531f 100644 --- a/src/hooks/useModal.ts +++ b/src/hooks/useModal.ts @@ -3,6 +3,7 @@ import { useState } from 'react'; export const useModal = () => { const [isOpen, setIsOpen] = useState(false); const [message, setMessage] = useState(''); + const handleModalOpen = (newMessage: string) => { setMessage(newMessage); setIsOpen(true); @@ -13,5 +14,11 @@ export const useModal = () => { setIsOpen(false); }; - return { isOpen, message, setIsOpen, handleModalClose, handleModalOpen }; + return { + isOpen, + message, + setIsOpen, + handleModalClose, + handleModalOpen, + }; }; diff --git a/src/hooks/useUpdateProject.ts b/src/hooks/useUpdateProject.ts index e8af0596..94fb4571 100644 --- a/src/hooks/useUpdateProject.ts +++ b/src/hooks/useUpdateProject.ts @@ -1,19 +1,18 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { putProject } from '../api/joinProject.api'; import { FormData } from '../models/createProject'; +import { MODAL_MESSAGE } from '../constants/modalMessage'; +import { ROUTES } from '../constants/routes'; +import { useNavigate } from 'react-router-dom'; interface UseUpdateProjectProps { id: number; - onSuccess?: () => void; - onError?: (error: unknown) => void; + handleModalOpen: (newMessage: string) => void; } -const useUpdateProject = ({ - id, - onSuccess, - onError, -}: UseUpdateProjectProps) => { +const useUpdateProject = ({ id, handleModalOpen }: UseUpdateProjectProps) => { const queryClient = useQueryClient(); + const navigate = useNavigate(); const mutation = useMutation({ mutationFn: (formData: FormData) => putProject(formData, id), @@ -22,11 +21,15 @@ const useUpdateProject = ({ queryKey: ['projectDataAll', id], exact: true, }); - onSuccess?.(); + handleModalOpen(MODAL_MESSAGE.ModifyProjectSuccess); + + setTimeout(() => { + navigate(`${ROUTES.projectDetail}/${id}`); + }, 3000); }, onError: (error) => { - console.error('Project update failed:', error); - onError?.(error); + handleModalOpen(MODAL_MESSAGE.ModifyProjectFail); + console.log(error); }, }); @@ -34,7 +37,7 @@ const useUpdateProject = ({ try { await mutation.mutateAsync(formData); } catch (error) { - console.error('Error updating project:', error); + console.error(error); throw error; } }; diff --git a/src/pages/apply/Apply.styled.ts b/src/pages/apply/Apply.styled.ts index b4420dd3..0e894941 100644 --- a/src/pages/apply/Apply.styled.ts +++ b/src/pages/apply/Apply.styled.ts @@ -6,24 +6,40 @@ export const Container = styled.div` margin: 0 auto; padding: 40px; font-family: Arial, sans-serif; + + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + padding: 25px; + } `; export const Title = styled.h1` font-size: 32px; font-weight: bold; margin-bottom: 50px; + + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + font-size: 27px; + } `; export const Subtitle = styled.h2` font-size: 25px; color: ${({ theme }) => theme.color.primary}; margin-bottom: 20px; + + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + font-size: 22px; + } `; export const Dates = styled.p` font-size: 20px; - color: #888; + color: ${({ theme }) => theme.color.placeholder}; margin-bottom: 50px; + + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + font-size: 16px; + } `; export const Form = styled.form` @@ -42,23 +58,11 @@ export const Section = styled.div` export const Label = styled.label` font-size: 20px; font-weight: bold; - color: #333; -`; + color: ${({ theme }) => theme.color.black}; -export const Input = styled.input` - width: 100%; - padding: 10px; - border: 1px solid ${({ theme }) => theme.color.border}; - border-radius: ${({ theme }) => theme.borderRadius.primary}; - margin-bottom: 10px; -`; - -export const TextArea = styled.textarea` - width: 100%; - height: 100px; - padding: 10px; - border: 1px solid ${({ theme }) => theme.color.border}; - border-radius: ${({ theme }) => theme.borderRadius.primary}; + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { + font-size: 18px; + } `; export const SubmitButton = styled(Button)` @@ -67,13 +71,3 @@ export const SubmitButton = styled(Button)` margin: 0 auto; cursor: pointer; `; - -export const FormError = styled.p` - margin-top: 0.3px; - font-size: 0.7rem; - color: ${({ theme }) => theme.color.red}; - position: absolute; - top: 100%; - left: 0; - white-space: nowrap; -`; diff --git a/src/pages/apply/Apply.tsx b/src/pages/apply/Apply.tsx index 7b8d72a7..6b017214 100644 --- a/src/pages/apply/Apply.tsx +++ b/src/pages/apply/Apply.tsx @@ -3,15 +3,16 @@ import Input from '../../components/projectFormComponents/inputComponent/inputCo import { useFieldArray, useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { formatDate } from '../../util/format'; -import { postApplicantProject } from '../../api/joinProject.api'; import { joinProject } from '../../models/joinProject'; import useGetProjectData from '../../hooks/useJoinProject'; import CareersComponent from '../../components/applyComponents/careersComponent/CareersComponent'; import PhoneComponent from '../../components/applyComponents/phoneComponent/PhoneComponent'; import LoadingSpinner from '../../components/common/loadingSpinner/LoadingSpinner'; -import { ROUTES } from '../../constants/routes'; +import Modal from '../../components/common/modal/Modal'; +import { useModal } from '../../hooks/useModal'; +import useApplyProject from '../../hooks/useApplyProject'; const ApplyScheme = z.object({ email: z @@ -39,9 +40,10 @@ export type ApplySchemeType = z.infer; const Apply = () => { const { projectId } = useParams(); - const navigate = useNavigate(); const id = Number(projectId); + const { isOpen, handleModalOpen, handleModalClose, message } = useModal(); const { data: projectData, isLoading, isFetching } = useGetProjectData(id); + const { applyProject } = useApplyProject({ id, handleModalOpen }); const { handleSubmit: onSubmitHandler, formState: { errors }, @@ -69,31 +71,7 @@ const Apply = () => { career: data.careers, }; - postApplicantProject(formData, id).then((status) => { - switch (status) { - case 201: - alert('지원서가 성공적으로 제출되었습니다.'); - navigate(ROUTES.main); - break; - case 400: - alert('잘못된 요청입니다.'); - break; - case 401: - alert('세션이 만료되었습니다. 로그인 해주세요.'); - break; - case 403: - alert('본인의 프로젝트는 지원할 수 없습니다.'); - break; - case 404: - alert('해당 페이지가 존재하지 않습니다'); - break; - case 500: - alert('서버 오류.'); - break; - default: - alert('알 수 없는 에러.'); - } - }); + applyProject(formData); }; if (!projectData) { @@ -158,6 +136,9 @@ const Apply = () => { 지원하기 + + {message} + ); }; diff --git a/src/pages/createProject/CreateProject.tsx b/src/pages/createProject/CreateProject.tsx index 2a9bd8c9..fe300b4f 100644 --- a/src/pages/createProject/CreateProject.tsx +++ b/src/pages/createProject/CreateProject.tsx @@ -4,12 +4,11 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import Input from '../../components/projectFormComponents/inputComponent/inputComponent'; import { CreateProjectFormValues, FormData } from '../../models/createProject'; -import { useNavigate } from 'react-router-dom'; import ProjectInformationInput from '../../components/projectFormComponents/projectInformationInput/ProjectInformationInput'; -import { createProject } from '../../api/joinProject.api'; import { useState } from 'react'; -import { useSaveSearchFiltering } from '../../hooks/useSaveSearchFiltering'; -import { ROUTES } from '../../constants/routes'; +import Modal from '../../components/common/modal/Modal'; +import { useModal } from '../../hooks/useModal'; +import useCreateProject from '../../hooks/useCreateProject'; export const createProjectScheme = z.object({ startDate: z @@ -55,7 +54,6 @@ export const createProjectScheme = z.object({ }); const CreateProject = () => { - const navigate = useNavigate(); const { handleSubmit: onSubmitHandler, formState: { errors }, @@ -77,7 +75,11 @@ const CreateProject = () => { }); const [isSubmit, setIsSubmit] = useState(false); - const { handleUpdateFilters } = useSaveSearchFiltering(); + const { isOpen, message, handleModalClose, handleModalOpen } = useModal(); + const { createProject } = useCreateProject({ + handleModalOpen, + setIsSubmit, + }); const handleSubmit = (data: z.infer) => { const formData: FormData = { @@ -94,14 +96,7 @@ const CreateProject = () => { description: data.markdownEditor, }; - createProject(formData).then((status: number) => { - if (status === 201) { - alert('프로젝트가 성공적으로 생성되었습니다.'); - setIsSubmit(true); - handleUpdateFilters('skillTag', []); - navigate(ROUTES.main); - } - }); + createProject(formData); }; return ( @@ -166,6 +161,9 @@ const CreateProject = () => { 제출 + + {message} + ); }; diff --git a/src/pages/modifyProject/ModifyProject.tsx b/src/pages/modifyProject/ModifyProject.tsx index d4ab75a7..50db6a42 100644 --- a/src/pages/modifyProject/ModifyProject.tsx +++ b/src/pages/modifyProject/ModifyProject.tsx @@ -4,32 +4,25 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import Input from '../../components/projectFormComponents/inputComponent/inputComponent'; import { CreateProjectFormValues, FormData } from '../../models/createProject'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import ProjectInformationInput from '../../components/projectFormComponents/projectInformationInput/ProjectInformationInput'; import { createProjectScheme } from '../createProject/CreateProject'; import useGetProjectData from '../../hooks/useJoinProject'; import { useEffect } from 'react'; import { formatDate } from '../../util/format'; import useUpdateProject from '../../hooks/useUpdateProject'; -import { useAlert } from '../../hooks/useAlert'; -import { ROUTES } from '../../constants/routes'; +import { useModal } from '../../hooks/useModal'; +import Modal from '../../components/common/modal/Modal'; const ModifyProject = () => { - const navigate = useNavigate(); const { projectId } = useParams(); const id = Number(projectId); - const { showAlert } = useAlert(); + const { isOpen, handleModalOpen, handleModalClose, message } = useModal(); const { data: projectData } = useGetProjectData(id); const { updateProject } = useUpdateProject({ id, - onSuccess: () => { - showAlert('수정 되었습니다.'); - navigate(`${ROUTES.projectDetail}/${id}`); - }, - onError: (error) => { - console.error('Error updating project:', error); - }, + handleModalOpen, }); const { @@ -67,11 +60,7 @@ const ModifyProject = () => { description: data.markdownEditor, }; - try { - await updateProject(formData); - } catch (e) { - console.error(e); - } + updateProject(formData); }; useEffect(() => { @@ -148,6 +137,9 @@ const ModifyProject = () => { 제출 + + {message} + ); }; diff --git a/src/pages/projectDetail/ProjectDetail.styled.ts b/src/pages/projectDetail/ProjectDetail.styled.ts index 4bf2c787..42b5752b 100644 --- a/src/pages/projectDetail/ProjectDetail.styled.ts +++ b/src/pages/projectDetail/ProjectDetail.styled.ts @@ -6,7 +6,7 @@ export const Container = styled.div` margin: 0 auto; padding: 40px; - @media (max-width: 870px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { padding: 25px; } `; @@ -23,7 +23,7 @@ export const Title = styled.h1` margin-bottom: 0.8rem; margin-top: 1rem; - @media (max-width: 870px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { font-size: 28px; } `; @@ -44,7 +44,7 @@ export const UserInfo = styled.div` gap: 12px; align-items: center; - @media (max-width: 870px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { flex-wrap: wrap; gap: 8px; } @@ -55,7 +55,7 @@ export const UserName = styled.div` font-weight: bold; cursor: pointer; - @media (max-width: 870px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { font-size: 18px; } `; @@ -64,7 +64,7 @@ export const PostDate = styled.div` font-size: 0.9rem; color: ${({ theme }) => theme.color.deepGrey}; - @media (max-width: 870px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { font-size: 0.8rem; } `; @@ -83,7 +83,7 @@ export const ViewCount = styled.div` color: ${({ theme }) => theme.color.deepGrey}; } - @media (max-width: 870px) { + @media screen and ${({ theme }) => theme.mediaQuery.tablet} { font-size: 0.9rem; svg { diff --git a/src/pages/projectDetail/ProjectDetail.tsx b/src/pages/projectDetail/ProjectDetail.tsx index 4cf570ba..dd5ab25c 100644 --- a/src/pages/projectDetail/ProjectDetail.tsx +++ b/src/pages/projectDetail/ProjectDetail.tsx @@ -11,6 +11,7 @@ import useAuthStore from '../../store/authStore'; import { ROUTES } from '../../constants/routes'; import { useModal } from '../../hooks/useModal'; import Modal from '../../components/common/modal/Modal'; +import LoadingSpinner from '../../components/common/loadingSpinner/LoadingSpinner'; const ProjectDetail = () => { const { projectId } = useParams(); @@ -20,10 +21,10 @@ const ProjectDetail = () => { const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const { userData } = useAuthStore((state) => state); - if (isLoading) return
Loading...
; - if (isFetching) return
isFetching...
; + if (isLoading) return ; + if (isFetching) return ; if (!data) { - return
데이터가 없습니다.
; + return ; } const handleApplyClick = () => {