diff --git a/src/api/http.api.ts b/src/api/http.api.ts index 2475c03a..cf02a9eb 100644 --- a/src/api/http.api.ts +++ b/src/api/http.api.ts @@ -12,7 +12,10 @@ export const createClient = (config?: AxiosRequestConfig) => { timeout: DEFAULT_TIMEOUT, headers: { 'content-type': 'application/json', - authorization: getTokens() ? `Bearer ${getTokens().accessToken}` : '', + authorization: + getTokens().accessToken || getTokens().refreshToken + ? `Bearer ${getTokens().accessToken}` + : '', }, withCredentials: true, ...config, diff --git a/src/api/joinProject.api.ts b/src/api/joinProject.api.ts index c2d87a3d..bd529a96 100644 --- a/src/api/joinProject.api.ts +++ b/src/api/joinProject.api.ts @@ -1,13 +1,21 @@ import { FormData } from '../models/createProject'; import { joinProject } from '../models/joinProject'; -import { ProjectDetailExtended } from '../models/projectDetail'; +import { ProjectDetailPlusExtended } from '../models/projectDetail'; import { httpClient } from './http.api'; export const getProjectData = async ( id: number -): Promise => { - const response = await httpClient.get(`/project/${id}`); - return response.data; +): Promise => { + try { + const response = await httpClient.get(`/project/${id}`); + if (response.status !== 200) { + throw new Error(`${response.status}`); + } + return response.data.data; + } catch (error) { + console.error(error); + throw error; + } }; export const postProject = async (formData: FormData) => { @@ -19,7 +27,7 @@ export const postApplicantProject = async ( formData: joinProject, id: number ) => { - const response = await httpClient.post(`/project/${id}/applicant`, formData); + const response = await httpClient.post(`/project/${id}/apply`, formData); return response.status; }; diff --git a/src/api/mypage.api.ts b/src/api/mypage.api.ts index 2168374f..2ce7237f 100644 --- a/src/api/mypage.api.ts +++ b/src/api/mypage.api.ts @@ -7,7 +7,7 @@ import { httpClient } from './http.api'; export const getMyInfo = async () => { try { - const response = await httpClient.get('/user/me'); + const response = await httpClient.get('/user'); return response.data; } catch (error) { console.error('mypage-myinfo:', error); diff --git a/src/api/projectSearchFiltering.api.ts b/src/api/projectSearchFiltering.api.ts index fcf4882f..9aa0a621 100644 --- a/src/api/projectSearchFiltering.api.ts +++ b/src/api/projectSearchFiltering.api.ts @@ -1,9 +1,13 @@ -import type { MethodTag, PositionTag, SkillTag } from '../models/tags'; +import type { + MethodTagHeader, + PositionTagHeader, + SillTagHeader, +} from '../models/tags'; import { httpClient } from './http.api'; export const getSkillTag = async () => { try { - const response = await httpClient.get('/skill-tag'); + const response = await httpClient.get('/skill-tag'); return response.data; } catch (e) { console.log('getSkillTag', e); @@ -12,7 +16,7 @@ export const getSkillTag = async () => { export const getPositionTag = async () => { try { - const response = await httpClient.get('/position-tag'); + const response = await httpClient.get('/position-tag'); return response.data; } catch (e) { console.log('getPositionTag', e); @@ -21,7 +25,7 @@ export const getPositionTag = async () => { export const getMethodTag = async () => { try { - const response = await httpClient.get('/method'); + const response = await httpClient.get('/method-type'); return response.data; } catch (e) { console.log('getMethodTag', e); diff --git a/src/components/command/CommandLayout.styled.ts b/src/components/command/CommandLayout.styled.ts new file mode 100644 index 00000000..a888cabf --- /dev/null +++ b/src/components/command/CommandLayout.styled.ts @@ -0,0 +1,33 @@ +import styled from 'styled-components'; + +export const Container = styled.div``; + +export const CommandCountsContainer = styled.div` + margin-bottom: 20px; +`; + +export const Count = styled.span``; + +export const CommandContainer = styled.div``; + +export const CommandInput = styled.div` + margin-bottom: 60px; +`; + +export const ShowReply = styled.div` + padding-left: 100px; +`; + +export const ShowReplyButton = styled.span` + display: flex; +`; + +export const Icon = styled.div``; +export const Content = styled.span` + margin-left: 10px; +`; + +export const ReplyContainer = styled.div` + padding-left: 100px; + margin-top: 20px; +`; diff --git a/src/components/command/CommandLayout.tsx b/src/components/command/CommandLayout.tsx new file mode 100644 index 00000000..13ed3db0 --- /dev/null +++ b/src/components/command/CommandLayout.tsx @@ -0,0 +1,46 @@ +import { useState } from 'react'; +import * as S from './CommandLayout.styled'; +import CommandComponent from './commandComponent/CommandComponent'; +import { IoIosArrowDown } from 'react-icons/io'; +import { IoIosArrowUp } from 'react-icons/io'; + +import CommandInput from './commandInput/CommandInput'; + +const CommandLayout = () => { + const [isShowReply, setIsShowReply] = useState(false); + + const handleClick = () => { + setIsShowReply(!isShowReply); + }; + + return ( + + + 댓글 {2}개 + + + + + + + + + + + + + {isShowReply ? : } + 답글 확인하기 + + + + {isShowReply && ( + + + + )} + + ); +}; + +export default CommandLayout; diff --git a/src/components/command/commandComponent/CommandComponent.styled.ts b/src/components/command/commandComponent/CommandComponent.styled.ts new file mode 100644 index 00000000..3c326dad --- /dev/null +++ b/src/components/command/commandComponent/CommandComponent.styled.ts @@ -0,0 +1,55 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + justify-content: space-between; + width: 100%; +`; + +export const Wrapper = styled.div` + display: flex; + flex-grow: 1; +`; + +export const CommandWrapper = styled.div` + margin-top: 4px; + flex: 1; +`; + +export const NickName = styled.p` + font-size: ${({ theme }) => theme.heading.xsSmall.fontSize}; + margin-left: 11px; + margin-bottom: 3px; +`; + +export const Command = styled.span` + margin-left: 11px; +`; + +export const ReplyContainer = styled.div` + margin-top: 11px; + margin-left: 11px; +`; + +export const ReplyButton = styled.div` + display: flex; + margin-top: 11px; +`; + +export const Icon = styled.div` + margin-left: 11px; +`; + +export const ReplyContent = styled.p` + margin-left: 10px; + margin-bottom: 10px; +`; + +export const CommandInput = styled.div` + text-indent: 20px; +`; + +export const ReplyInput = styled.div` + width: 100%; + padding-left: 15px; +`; diff --git a/src/components/command/commandComponent/CommandComponent.tsx b/src/components/command/commandComponent/CommandComponent.tsx new file mode 100644 index 00000000..045dff05 --- /dev/null +++ b/src/components/command/commandComponent/CommandComponent.tsx @@ -0,0 +1,79 @@ +import { useState } from 'react'; +import * as S from './CommandComponent.styled'; +import Avatar from '../../common/avatar/Avatar'; +import DefaultImg from '../../../assets/defaultImg.png'; +import { IoChatbubbleEllipsesOutline } from 'react-icons/io5'; +import CommandInput from '../commandInput/CommandInput'; +import { CiMenuKebab } from 'react-icons/ci'; +import DropDown from '../../common/dropDown/DropDown'; +import DropDownItem from '../../common/dropDown/DropDownItem'; +import useDropDownItem from '../../../hooks/useDropDownItem'; + +interface CommandLayoutProps { + data: []; + reply?: boolean; +} + +const command = '안녕하세요'; + +const CommandComponent = ({ data, reply }: CommandLayoutProps) => { + const [showReplyInput, setShowReplyInput] = useState(false); + const [showMenu, setShowMenu] = useState(false); + + const { onReport, onEdit, onDelete, isEditMode } = useDropDownItem(); + + const handleClick = () => { + setShowReplyInput(!showReplyInput); + }; + + console.log(isEditMode); + + const onClick = () => { + setShowMenu(!showMenu); + }; + + return ( + + {/* 전체를 map으로 감싸 하기 */} + + + + SeungYeon + {isEditMode ? ( + + ) : ( + {command} + )} + {!reply && ( + + + + + 댓글 달기 + + )} + {showReplyInput && ( + + + + )} + + + }> + + + + ); +}; + +export default CommandComponent; diff --git a/src/components/command/commandInput/CommandInput.styled.ts b/src/components/command/commandInput/CommandInput.styled.ts new file mode 100644 index 00000000..d254e270 --- /dev/null +++ b/src/components/command/commandInput/CommandInput.styled.ts @@ -0,0 +1,31 @@ +import styled from 'styled-components'; +import Button from '../../common/Button/Button'; + +export const InputContainer = styled.div` + display: flex; + margin-left: 8px; +`; + +export const Input = styled.input` + width: 100%; +`; + +export const InputWrapper = styled.div` + width: 100%; + margin-left: 5px; + margin-top: 7px; +`; + +export const ButtonWrapper = styled.div` + display: flex; + justify-content: flex-end; + margin-top: 3px; +`; +export const Line = styled.hr<{ $isFocused: boolean }>` + opacity: ${({ $isFocused }) => ($isFocused ? 1.0 : 0.2)}; + border: ${({ $isFocused }) => ($isFocused ? 2 : 1)}; +`; + +export const ButtonCancel = styled(Button)``; + +export const ButtonSubmit = styled(Button)``; diff --git a/src/components/command/commandInput/CommandInput.tsx b/src/components/command/commandInput/CommandInput.tsx new file mode 100644 index 00000000..e2a8f34b --- /dev/null +++ b/src/components/command/commandInput/CommandInput.tsx @@ -0,0 +1,95 @@ +import * as S from './CommandInput.styled'; +import { useMyProfileInfo } from '../../../hooks/useMyInfo'; +import { formatImgPath } from '../../../util/formatImgPath'; +import DefaultImg from '../../../assets/defaultImg.png'; +import Avatar from '../../common/avatar/Avatar'; +import { useForm } from 'react-hook-form'; +import useInputFocus from '../../../hooks/useInputFocus'; +import { useEffect } from 'react'; + +type FormValue = { + commandInput: string; +}; + +interface CommandInputProps { + reply?: boolean; + isEditMode?: boolean; + onEdit?: () => void; + command?: string; +} + +const CommandInput = ({ + reply, + isEditMode, + onEdit, + command, +}: CommandInputProps) => { + const { myData } = useMyProfileInfo(); + const { + handleSubmit: onSubmitHandler, + watch, + register, + setValue, + } = useForm(); + const { isFocused, handleFocus, handleClick } = useInputFocus(); + + const profileImg = myData?.profileImg + ? `${import.meta.env.VITE_APP_IMAGE_CDN_URL}/${formatImgPath( + myData.profileImg + )}?w=86&h=86&fit=crop&crop=entropy&auto=format,enhance&q=60` + : DefaultImg; + + const hasInput = Boolean(watch('commandInput', '')); + + const handleSubmit = (data) => { + // reply, edit-reply, command, command-reply + console.log(data); + }; + + useEffect(() => { + if (command) setValue('commandInput', command); + }, [command, setValue]); + + return ( + + {!isEditMode && } + +
e.preventDefault() + } + > + + + {isFocused && ( + + + 취소 + + + {isEditMode ? '수정' : '등록'} + + + )} + +
+
+ ); +}; + +export default CommandInput; diff --git a/src/components/common/dropDown/DropDownItem.styled.ts b/src/components/common/dropDown/DropDownItem.styled.ts new file mode 100644 index 00000000..3864d4ae --- /dev/null +++ b/src/components/common/dropDown/DropDownItem.styled.ts @@ -0,0 +1,29 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + position: absolute; + top: -20px; + right: 0; + width: 120px; + background-color: #f7f7f7; + border: 1px solid #e0e0e0; + border-radius: 4px; + overflow: hidden; + z-index: 999; +`; + +export const Item = styled.div` + padding: 12px 16px; + font-size: 14px; + color: #333; + cursor: pointer; + background-color: #f7f7f7; + + &:hover { + background-color: #eaeaea; + } + + & + & { + border-top: 1px solid #ccc; + } +`; diff --git a/src/components/common/dropDown/DropDownItem.tsx b/src/components/common/dropDown/DropDownItem.tsx new file mode 100644 index 00000000..b8722e39 --- /dev/null +++ b/src/components/common/dropDown/DropDownItem.tsx @@ -0,0 +1,30 @@ +import useDropDownItem from '../../../hooks/useDropDownItem'; +import * as S from './DropDownItem.styled'; + +interface DropdownProps { + isEditMode?: boolean; + commandId?: number; + onReport?: () => void; + onEdit?: () => void; + onDelete?: () => void; +} + +const DropDownItem = ({ + onReport, + onEdit, + onDelete, + commandId, + isEditMode, +}: DropdownProps) => { + return ( + + 신고하기 + + {isEditMode ? '수정 취소하기' : '수정하기'} + + 삭제하기 + + ); +}; + +export default DropDownItem; diff --git a/src/components/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx b/src/components/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx index 07864773..c5973dab 100644 --- a/src/components/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx +++ b/src/components/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx @@ -7,14 +7,14 @@ import { PROJECT_DATA } from '../../../constants/projectConstants'; import { CreateProjectFormValues } from '../../../models/createProject'; import * as S from './ProjectInformationInput.styled'; import { useSearchFilteringSkillTag } from '../../../hooks/useSearchFilteringSkillTag'; -import { ProjectDetailExtended } from '../../../models/projectDetail'; +import { ProjectDetailPlusExtended } from '../../../models/projectDetail'; import Input from '../inputComponent/InputComponent'; interface ProjectInformationProps { errors: FieldErrors; control: Control; setValue: UseFormSetValue; - apiData?: ProjectDetailExtended; + apiData?: ProjectDetailPlusExtended; isSubmit?: boolean; setIsSubmit?: Dispatch>; } @@ -70,7 +70,7 @@ const ProjectInformationInput = ({ errors={errors} setValue={setValue} methodTagsData={methodTagsData} - apiDataMethodId={apiData?.Method.id} + apiDataMethodId={apiData?.methodType.id} /> @@ -83,7 +83,7 @@ const ProjectInformationInput = ({ errors={errors} setValue={setValue} positionTagsData={positionTagsData} - apiDataPosition={apiData?.ProjectPositionTag} + apiDataPosition={apiData?.positions} /> @@ -93,7 +93,7 @@ const ProjectInformationInput = ({ name='languages' errors={errors} setValue={setValue} - apiDataSkillTags={apiData?.skillTags} + apiDataSkillTags={apiData?.skills} /> ); diff --git a/src/components/projectFormComponents/projectInformationInput/fieldCategoryComponent/FieldCategoryComponent.tsx b/src/components/projectFormComponents/projectInformationInput/fieldCategoryComponent/FieldCategoryComponent.tsx index 2ec7c390..2f1f1f5a 100644 --- a/src/components/projectFormComponents/projectInformationInput/fieldCategoryComponent/FieldCategoryComponent.tsx +++ b/src/components/projectFormComponents/projectInformationInput/fieldCategoryComponent/FieldCategoryComponent.tsx @@ -10,7 +10,7 @@ interface FieldCategoryComponentProps { errors: FieldErrors; name: string; setValue: UseFormSetValue; - methodTagsData: MethodTag[]; + methodTagsData: MethodTag[] | undefined; apiDataMethodId?: number; } @@ -40,7 +40,7 @@ const FieldCategoryComponent = ({ return ( - {methodTagsData.map((data, idx) => { + {methodTagsData?.map((data, idx) => { return ( ; - apiDataSkillTags: SkillTag[] | undefined; + apiDataSkillTags: Skill[] | undefined; } const LanguageComponent = ({ diff --git a/src/components/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx b/src/components/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx index 09323891..19d3f92c 100644 --- a/src/components/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx +++ b/src/components/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import * as S from './PositionComponent.styled'; import { FieldErrors } from 'react-hook-form'; import { PositionTag } from '../../../../models/tags'; -import { ProjectPositionTag } from '../../../../models/projectDetail'; +import { Position } from '../../../../models/projectDetail'; interface MozipCategoryComponentProps { selectedMozip: number[]; @@ -11,7 +11,7 @@ interface MozipCategoryComponentProps { name: string; setValue: any; positionTagsData: PositionTag[]; - apiDataPosition: ProjectPositionTag[] | undefined; + apiDataPosition: Position[] | undefined; } const MozipCategoryComponent = ({ @@ -41,7 +41,7 @@ const MozipCategoryComponent = ({ useEffect(() => { const positionTagIdList: number[] = []; apiDataPosition?.map((tag) => { - positionTagIdList.push(tag.positionTagId); + positionTagIdList.push(tag.id); }); setSelectedMozip(positionTagIdList); setValue('position', positionTagIdList); diff --git a/src/components/projectFormComponents/projectInformationText/ProjectInformation.tsx b/src/components/projectFormComponents/projectInformationText/ProjectInformation.tsx index dad4caa3..93c98da4 100644 --- a/src/components/projectFormComponents/projectInformationText/ProjectInformation.tsx +++ b/src/components/projectFormComponents/projectInformationText/ProjectInformation.tsx @@ -2,13 +2,13 @@ import * as S from './ProjectInformation.styled'; import { PROJECT_DATA_GET } from '../../../constants/projectConstants'; import beginner from '/src/assets/beginner.svg'; import { - ProjectDetail, - ProjectDetailExtended, + ProjectDetailPlus, + ProjectDetailPlusExtended, } from '../../../models/projectDetail'; import { formatDate } from '../../../util/format'; interface ProjectInformationProps { - data: ProjectDetailExtended; + data: ProjectDetailPlusExtended; } const ProjectInformation = ({ data }: ProjectInformationProps) => { @@ -26,35 +26,33 @@ const ProjectInformation = ({ data }: ProjectInformationProps) => { {PROJECT_DATA_GET.map((input, index) => ( {input.label} - {data[input.name as keyof ProjectDetail]} + {data[input.name as keyof ProjectDetailPlus]} ))} 모집 분야 - {data.ProjectPositionTag.map( - (position) => position.PositionTag.name - ).join(', ')} + {data.positions.map((position) => position.name).join(', ')} 진행 방식 - {data.Method.name} + {data.methodType.name} 사용 언어 - {data.ProjectSkillTag.map((skillTag) => ( - + {data.skills.map((skillTag) => ( + {skillTag.SkillTag.name} - {skillTag.SkillTag.name} + {skillTag.skillName} ))} diff --git a/src/constants/modalMessage.ts b/src/constants/modalMessage.ts index 5e5fcdf6..ebc0c97d 100644 --- a/src/constants/modalMessage.ts +++ b/src/constants/modalMessage.ts @@ -22,4 +22,5 @@ export const MODAL_MESSAGE = { createProjectFail: '공고 생성을 실패 했습니다.', applyProjectSuccess: '해당 공고에 지원을 완료 되었습니다.', applyProjectFail: '해당 공고에 지원을 실패 되었습니다.', + projectDetailFail: '해당 공고가 존재하지 않습니다.', } as const; diff --git a/src/hooks/useDropDownItem.ts b/src/hooks/useDropDownItem.ts new file mode 100644 index 00000000..9acde92c --- /dev/null +++ b/src/hooks/useDropDownItem.ts @@ -0,0 +1,21 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +const useDropDownItem = (commandId?: number) => { + const navigate = useNavigate(); + const [isEditMode, setIsEditMode] = useState(false); + + const onReport = () => { + console.log('신고하기'); + // navigate(''); + }; + + const onEdit = () => { + setIsEditMode(!isEditMode); + }; + + const onDelete = () => {}; + return { onReport, onEdit, onDelete, isEditMode }; +}; + +export default useDropDownItem; diff --git a/src/hooks/useInputFocus.ts b/src/hooks/useInputFocus.ts new file mode 100644 index 00000000..81c70006 --- /dev/null +++ b/src/hooks/useInputFocus.ts @@ -0,0 +1,17 @@ +import { useState } from 'react'; + +const useInputFocus = () => { + const [isFocused, setIsFocused] = useState(false); + + const handleFocus = () => { + setIsFocused(true); + }; + + const handleClick = () => { + setIsFocused(false); + }; + + return { isFocused, handleFocus, handleClick }; +}; + +export default useInputFocus; diff --git a/src/hooks/useSearchFilteringSkillTag.ts b/src/hooks/useSearchFilteringSkillTag.ts index 10203fa5..d99c82a7 100644 --- a/src/hooks/useSearchFilteringSkillTag.ts +++ b/src/hooks/useSearchFilteringSkillTag.ts @@ -10,7 +10,7 @@ import type { MethodTag, PositionTag, SkillTag } from '../models/tags'; export const useSearchFilteringSkillTag = () => { const [skillTagsData, setSkillTagsData] = useState([]); const [positionTagsData, setPositionTagsData] = useState([]); - const [methodTagsData, setMethodTagsData] = useState([]); + const [methodTagsData, setMethodTagsData] = useState(); const queries = useQueries({ queries: [ @@ -38,10 +38,10 @@ export const useSearchFilteringSkillTag = () => { const [skillQuery, positionQuery, methodQuery] = queries; useEffect(() => { - if (!skillQuery.data || !positionQuery.data || !methodQuery.data) return; - setSkillTagsData(skillQuery.data); - setPositionTagsData(positionQuery.data); - setMethodTagsData(methodQuery.data); + if (!skillQuery.data || !positionQuery.data) return; + setSkillTagsData(skillQuery.data.data); + setPositionTagsData(positionQuery.data.data); + setMethodTagsData(methodQuery.data?.data); }, [skillQuery.data, positionQuery.data, methodQuery.data]); return { skillTagsData, positionTagsData, methodTagsData }; diff --git a/src/models/createProject.ts b/src/models/createProject.ts index 24eb88b0..75972e02 100644 --- a/src/models/createProject.ts +++ b/src/models/createProject.ts @@ -5,14 +5,15 @@ export type CreateProjectFormValues = z.infer; export interface FormData { title: string; + description: string; totalMember: number; - recruitmentStartDate: string; - recruitmentEndDate: string; startDate: string; - positionTagId: number[]; estimatedPeriod: string; - methodId: number; + methodType: number; isBeginner?: boolean; - skillTagId: number[]; - description: string; + recruitmentStartDate: string; + recruitmentEndDate: string; + authorId: number; + positionTagIds: number[]; + skillTagIds: number[]; } diff --git a/src/models/projectDetail.ts b/src/models/projectDetail.ts index ca03690d..427aecc7 100644 --- a/src/models/projectDetail.ts +++ b/src/models/projectDetail.ts @@ -3,11 +3,7 @@ import { joinProject } from './joinProject'; export interface User { id: number; nickname: string; - email: string; - bio: string | null; - profileImg: string; - createdAt: string; - updatedAt: string; + img: string; } export interface SkillTag { @@ -67,3 +63,42 @@ export interface ProjectDetailExtended extends ProjectDetail { ProjectPositionTag: ProjectPositionTag[]; Applicant: joinProject[]; } + +export interface Position { + id: number; + name: string; +} + +export interface Skill { + id: number; + skillName: string; + skillImg: string; +} + +export interface ProjectDetailPlus { + id: number; + title: string; + description: string; + totalMember: number; + startDate: string; + estimatedPeriod: string; + views: number; + isBeginner: boolean; + isDone: boolean; + recruitmentStartDate: string; + recruitmentEndDate: string; + authorId: number; +} + +export interface ProjectDetailPlusExtended extends ProjectDetailPlus { + user: User; + methodType: Method; + positions: Position[]; + skills: Skill[]; +} + +export interface dataPlus { + success: boolean; + message: string; + data: ProjectDetailPlus; +} diff --git a/src/models/tags.ts b/src/models/tags.ts index 46bc0203..2bb0df7d 100644 --- a/src/models/tags.ts +++ b/src/models/tags.ts @@ -16,3 +16,21 @@ export interface MethodTag { name: string; createdAt: string; } + +export interface SillTagHeader { + success: boolean; + message: string; + data: SkillTag[]; +} + +export interface PositionTagHeader { + success: boolean; + message: string; + data: PositionTag[]; +} + +export interface MethodTagHeader { + success: boolean; + message: string; + data: MethodTag[]; +} diff --git a/src/pages/createProject/CreateProject.tsx b/src/pages/createProject/CreateProject.tsx index bc747d58..6d235c7d 100644 --- a/src/pages/createProject/CreateProject.tsx +++ b/src/pages/createProject/CreateProject.tsx @@ -10,6 +10,7 @@ import Modal from '../../components/common/modal/Modal'; import { useModal } from '../../hooks/useModal'; import useCreateProject from '../../hooks/useCreateProject'; import LoadingSpinner from '../../components/common/loadingSpinner/LoadingSpinner'; +import useAuthStore from '../../store/authStore'; export const createProjectScheme = z.object({ startDate: z @@ -81,6 +82,14 @@ const CreateProject = () => { handleModalOpen, setIsSubmit, }); + const userId = useAuthStore((state) => state.userData?.id); + if (!userId) { + return ( + + {message} + + ); + } const handleSubmit = (data: z.infer) => { const formData: FormData = { @@ -89,13 +98,15 @@ const CreateProject = () => { recruitmentStartDate: data.startDate, recruitmentEndDate: data.endDate, startDate: data.startDatePre, - positionTagId: data.position, + positionTagIds: data.position, estimatedPeriod: `${data.duration}개월`, - methodId: data.field, + methodType: data.field, isBeginner: data.newBy, - skillTagId: data.languages, + skillTagIds: data.languages, description: data.markdownEditor, + authorId: userId, }; + console.log(formData); createProject(formData); }; diff --git a/src/pages/modifyProject/ModifyProject.tsx b/src/pages/modifyProject/ModifyProject.tsx index 000fa204..428575c9 100644 --- a/src/pages/modifyProject/ModifyProject.tsx +++ b/src/pages/modifyProject/ModifyProject.tsx @@ -24,6 +24,7 @@ const ModifyProject = () => { id, handleModalOpen, }); + const userId = projectData?.user.id; const { handleSubmit: onSubmitHandler, @@ -45,6 +46,29 @@ const ModifyProject = () => { }, }); + console.log(projectData); + + useEffect(() => { + if (projectData) { + setValue('startDatePre', formatDate(projectData.startDate)); + setValue('startDate', formatDate(projectData.recruitmentStartDate)); + setValue('endDate', formatDate(projectData.recruitmentEndDate)); + setValue('title', projectData.title); + setValue('newBy', projectData.isBeginner); + setValue('maxVolunteers', projectData.totalMember); + setValue('duration', Number(projectData.estimatedPeriod.slice(0, 1))); + setValue('markdownEditor', projectData.description); + } + }, [projectData, setValue]); + + if (!userId) { + return ( + + {message} + + ); + } + const handleSubmit = async (data: z.infer) => { const formData: FormData = { title: data.title, @@ -52,30 +76,18 @@ const ModifyProject = () => { recruitmentStartDate: data.startDate, recruitmentEndDate: data.endDate, startDate: data.startDatePre, - positionTagId: data.position, + positionTagIds: data.position, estimatedPeriod: `${data.duration}개월`, - methodId: data.field, + methodType: data.field, isBeginner: data.newBy, - skillTagId: data.languages, + skillTagIds: data.languages, description: data.markdownEditor, + authorId: userId, }; updateProject(formData); }; - useEffect(() => { - if (projectData) { - setValue('startDatePre', formatDate(projectData.startDate)); - setValue('startDate', formatDate(projectData.recruitmentStartDate)); - setValue('endDate', formatDate(projectData.recruitmentEndDate)); - setValue('title', projectData.title); - setValue('newBy', projectData.isBeginner); - setValue('maxVolunteers', projectData.totalMember); - setValue('duration', Number(projectData.estimatedPeriod.slice(0, 1))); - setValue('markdownEditor', projectData.description); - } - }, [projectData]); - return ( 프로젝트 생성 diff --git a/src/pages/projectDetail/ProjectDetail.tsx b/src/pages/projectDetail/ProjectDetail.tsx index 120e6712..0bc7d71b 100644 --- a/src/pages/projectDetail/ProjectDetail.tsx +++ b/src/pages/projectDetail/ProjectDetail.tsx @@ -3,25 +3,42 @@ import ProjectInformation from '../../components/projectFormComponents/projectIn import useGetProjectData from '../../hooks/useJoinProject'; import * as S from './ProjectDetail.styled'; import { formatDate } from '../../util/format'; -import Button from '../../components/common/Button/Button'; import MarkdownEditorView from '../../components/projectFormComponents/editor/MarkdownEditorView'; -import Avatar from '../../components/common/avatar/Avatar'; import { EyeIcon } from '@heroicons/react/24/outline'; import useAuthStore from '../../store/authStore'; import { ROUTES } from '../../constants/routes'; import LoadingSpinner from '../../components/common/loadingSpinner/LoadingSpinner'; +import Modal from '../../components/common/modal/Modal'; +import { useModal } from '../../hooks/useModal'; +import { MODAL_MESSAGE } from '../../constants/modalMessage'; +import { useEffect } from 'react'; +import CommandLayout from '../../components/command/CommandLayout'; +import Avatar from '../../components/common/avatar/Avatar'; +import Button from '../../components/common/Button/Button'; const ProjectDetail = () => { const { projectId } = useParams(); const id = Number(projectId); const navigate = useNavigate(); + const { isOpen, message, handleModalClose, handleModalOpen } = useModal(); const { data, isLoading, isFetching } = useGetProjectData(id); const { userData } = useAuthStore((state) => state); + useEffect(() => { + if (!data) { + handleModalOpen(MODAL_MESSAGE.projectDetailFail); + } + }, [data, handleModalOpen]); + if (isLoading) return ; if (isFetching) return ; + if (!data) { - return ; + return ( + + {message} + + ); } const handleApplyClick = () => { @@ -29,7 +46,7 @@ const ProjectDetail = () => { }; const handleMovetoUserPage = () => { - const userId = data.User.id; + const userId = data.user.id; navigate(`/user/${userId}`); }; @@ -40,11 +57,11 @@ const ProjectDetail = () => { {data.title} - + - {data.User.nickname} + {data.user.nickname} {formatDate(data.recruitmentEndDate)} @@ -63,7 +80,7 @@ const ProjectDetail = () => { - {userData?.id !== data.User.id ? ( + {userData?.id !== data.user.id ? (