From 1e3597fab5a573d0e6c1b1cb2084f76b803dc5e4 Mon Sep 17 00:00:00 2001 From: heejung0413 Date: Sat, 20 Apr 2024 21:08:43 +0900 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=90=9BFix:=20comment=20API=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/@types/Comment.ts | 52 ++++---- src/api/@types/Retrospectives.ts | 15 ++- src/api/services/Comment.ts | 31 +++-- src/api/services/Retrospectives.ts | 11 +- .../writeRetro/revise/RetroImageUploader.tsx | 36 ++++-- .../writeRetro/revise/ReviseSetting.tsx | 44 +++++-- .../writeRetro/task/ReviseCommentModal.tsx | 53 ++++++++ src/components/writeRetro/task/TeamTask.tsx | 2 +- .../task/taskMessage/TeamTaskMessage.tsx | 122 +++++++++++++----- src/pages/RevisePage.tsx | 5 +- src/styles/writeRetroStyles/Layout.style.ts | 8 +- 11 files changed, 273 insertions(+), 106 deletions(-) create mode 100644 src/components/writeRetro/task/ReviseCommentModal.tsx diff --git a/src/api/@types/Comment.ts b/src/api/@types/Comment.ts index 5e12045..2a46852 100644 --- a/src/api/@types/Comment.ts +++ b/src/api/@types/Comment.ts @@ -1,48 +1,50 @@ -export interface GetCommentRequest { - id: string; +//post +export interface PostCommentRequest { + sectionId: number; + commentContent: string; } -export interface GetCommentResponse { +export interface PostCommentResponse { code: number; message: string; - data: { - id: number; - content: string; - }; + data: PostCommentData; } -//put -export interface PutCommentRequest { +export interface PostCommentData { id: number; + userId: number; + sectionId: number; + commentContent: string; } -//delete -export interface DeleteCommentRequest { - id: number; +//put +export interface PutCommentRequest { + commentId: number; + commentContent: string; } -export interface DeleteCommentResponse { +export interface PutCommentResponse { code: number; message: string; - data: object; + data: PutCommentData; } -//GetAllComment -export interface AllGetCommentResponse { - code: number; - message: string; - data: CommentData[]; +export interface PutCommentData { + commentId: number; + content: string; } -export interface CommentData { - id: number; - comment: string; +//delete +export interface DeleteCommentRequest { + commentId: number; } -//Post +export interface DeleteCommentResponse { + code: number; +} export interface CommentClient { - getComment(request: GetCommentRequest): Promise; + post(request: PostCommentRequest): Promise; delete(request: DeleteCommentRequest): Promise; - getAllComment(): Promise; + put(request: PutCommentRequest): Promise; } diff --git a/src/api/@types/Retrospectives.ts b/src/api/@types/Retrospectives.ts index 2d30cf3..d0da201 100644 --- a/src/api/@types/Retrospectives.ts +++ b/src/api/@types/Retrospectives.ts @@ -19,7 +19,7 @@ export interface RetrospectiveData { userId: number; leaderName: string; description: string; - status: keyof TStatus; + status: string; thumbnail: string; } @@ -84,7 +84,7 @@ export interface DeleteRetrospectiveRequest { } //put -export interface PutRetrospectiveRequest { +export interface PutTeamRetrospectiveRequest { retrospectiveId: number; title: string; teamId?: number; @@ -93,6 +93,14 @@ export interface PutRetrospectiveRequest { thumbnail?: string; } +export interface PutPersonalRetrospectiveRequest { + retrospectiveId: number; + title: string; + description: string; + status: keyof TStatus; + thumbnail?: string; +} + export interface RetrospectiveResponse { code: number; message: string; @@ -127,6 +135,7 @@ export interface RetrospectivesClient { create(request: PostRetrospectivesRequest): Promise; get(request: GetRetrospectiveRequest): Promise; delete(request: DeleteRetrospectiveRequest): Promise; - put(request: PutRetrospectiveRequest): Promise; + putTeam(request: PutTeamRetrospectiveRequest): Promise; + putPersonal(request: PutPersonalRetrospectiveRequest): Promise; patch(request: PatchRetrospectiveRequest): Promise; } diff --git a/src/api/services/Comment.ts b/src/api/services/Comment.ts index 055e099..cd0db7f 100644 --- a/src/api/services/Comment.ts +++ b/src/api/services/Comment.ts @@ -1,16 +1,31 @@ -import { CommentClient } from '../@types/Comment'; -import { mswInstance } from '../client'; +import { CommentClient, DeleteCommentRequest, PostCommentRequest, PostCommentResponse } from '../@types/Comment'; +import axiosInstance from '../axiosConfig'; const ROUTE = '/comments'; export const CommentService: CommentClient = { - getComment: async id => { - return await mswInstance.get(`/api/${ROUTE}/${id}`); + post: async (request: PostCommentRequest): Promise => { + try { + const response = await axiosInstance.post(`${ROUTE}`, request); + return response.data; + } catch (error) { + throw new Error(error as string); + } }, - delete: async id => { - return await mswInstance.delete(`/api/${ROUTE}/${id}`); + delete: async ({ commentId }: DeleteCommentRequest) => { + try { + const response = await axiosInstance.delete(`${ROUTE}/${commentId}`); + return response.data; + } catch (error) { + throw new Error(error as string); + } }, - getAllComment: async () => { - return await mswInstance.get(`api/${ROUTE}`); + put: async ({ commentId, ...request }) => { + try { + const response = await axiosInstance.put(`${ROUTE}/${commentId}`, request); + return response.data; + } catch (error) { + throw new Error(error as string); + } }, }; diff --git a/src/api/services/Retrospectives.ts b/src/api/services/Retrospectives.ts index c130d58..0bd58a2 100644 --- a/src/api/services/Retrospectives.ts +++ b/src/api/services/Retrospectives.ts @@ -49,7 +49,16 @@ export const RetrospectiveService: RetrospectivesClient = { } }, - put: async ({ retrospectiveId }, ...request) => { + putTeam: async ({ retrospectiveId, ...request }) => { + try { + const response = await axiosInstance.put(`${ROUTE}/${retrospectiveId}`, request); + return response.data; + } catch (error) { + throw new Error(error as string); + } + }, + + putPersonal: async ({ retrospectiveId, ...request }) => { try { const response = await axiosInstance.put(`${ROUTE}/${retrospectiveId}`, request); return response.data; diff --git a/src/components/writeRetro/revise/RetroImageUploader.tsx b/src/components/writeRetro/revise/RetroImageUploader.tsx index bc4959a..5510765 100644 --- a/src/components/writeRetro/revise/RetroImageUploader.tsx +++ b/src/components/writeRetro/revise/RetroImageUploader.tsx @@ -1,40 +1,48 @@ -import { ChangeEventHandler, FC, MouseEventHandler, useRef } from 'react'; +import { ChangeEventHandler, FC, MouseEventHandler, useRef, useState } from 'react'; import { MdOutlineFileUpload } from 'react-icons/md'; import { Button, Image } from '@chakra-ui/react'; +import { v4 as uuidv4 } from 'uuid'; interface Props { image: string; - setImage: (image: string) => void; + setImage: (image: File | null, uuid: string) => void; } const RetroImageUploader: FC = ({ image, setImage }) => { + const [preview, setPreview] = useState(null); + const [_, setImageUUID] = useState(null); // 상태를 활용할 수 있도록 수정 + const inputRef = useRef(null); const handleUploadButtonClick: MouseEventHandler = () => { inputRef.current?.click(); }; const handleImageChange: ChangeEventHandler = async event => { - const files = event.target.files; - if (!files) return; + const files = event.target.files?.[0]; + if (files) { - const file = files[0]; - const imageUrl = URL.createObjectURL(file); - const imageUrlString: string = imageUrl; - setImage(imageUrlString); + const reader = new FileReader(); + const uuid = uuidv4(); + setImage(files, uuid); + + reader.onloadend = () => { + const result = reader.result as string; + setPreview(result); + setImageUUID(uuid); + }; + reader.readAsDataURL(files); } }; const DeleteImage: MouseEventHandler = () => { - setImage(''); + setPreview(null); + setImageUUID(null); + setImage(null, ''); }; return ( <> - {image ? ( - - ) : ( - - )} +
+ + + + ); +}; + +export default ReviseCommentModal; diff --git a/src/components/writeRetro/task/TeamTask.tsx b/src/components/writeRetro/task/TeamTask.tsx index 29969c7..2f6560d 100644 --- a/src/components/writeRetro/task/TeamTask.tsx +++ b/src/components/writeRetro/task/TeamTask.tsx @@ -123,7 +123,7 @@ const TeamTask: FC = ({ section }) => { {messaged ? : } - 0 + {section.comments.length} {/* DaysLeft */} diff --git a/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx b/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx index 23b3686..ce2cc0a 100644 --- a/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx +++ b/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx @@ -1,7 +1,23 @@ import { ChangeEvent, FC, useState } from 'react'; import { CgProfile } from 'react-icons/cg'; -import { Flex, Modal, ModalCloseButton, ModalContent, ModalOverlay, useDisclosure } from '@chakra-ui/react'; +import { FaRegTrashAlt } from 'react-icons/fa'; +import { + Button, + Flex, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverHeader, + PopoverTrigger, + Portal, +} from '@chakra-ui/react'; +import ReviseCommentModal from '../ReviseCommentModal'; +import { PostCommentData } from '@/api/@types/Comment'; import { sectionData } from '@/api/@types/Section'; +import { CommentService } from '@/api/services/Comment'; +import { useCustomToast } from '@/hooks/useCustomToast'; import * as S from '@/styles/writeRetroStyles/Layout.style'; interface Props { @@ -9,62 +25,100 @@ interface Props { } const TeamTaskMessage: FC = ({ section }) => { - const { isOpen, onOpen, onClose } = useDisclosure(); const [value, setValue] = useState(''); + const [comment, setComment] = useState(); + const toast = useCustomToast(); const handleChange = (e: ChangeEvent) => { setValue(e.target.value); e.target.style.height = 'auto'; e.target.style.height = `${e.target.scrollHeight}px`; }; + const handlePostComment = async () => { + try { + const response = await CommentService.post({ sectionId: section.sectionId, commentContent: value }); + setComment(response.data); + console.log(comment); + } catch (e) { + toast.error(e); + } + }; + + const handleDeleteComment = async (id: number) => { + try { + await CommentService.delete({ commentId: id }); + } catch (e) { + toast.error(e); + } + }; + return ( <> {/* TaskMessage */} {/* TaskMessageTop */} - 0개의 댓글 + {section.comments.length}개의 댓글 {/* TaskMessages */}
- {section.comments.map(data => ( + {section.comments.map(section => ( {/* TaskMessageTop */} - - - {data.username} + + + + {section.username} + {/* 1일 전 */} -
- + + 삭제 - -
-
+ + + + + + + 삭제요청 + + + + 선택한 회고 카드를 삭제하시겠습니까? + + + + + + + +
+ + + + {section.content} + {/* (수정됨) */} + + + + + {/* TaskTextModal */} + + + {/* TaskMessageMain */} - - {data.content} - {/* TeamActionItemsTask */} - - {/* MessageModal */} - - - - - - ))}
@@ -73,7 +127,7 @@ const TeamTaskMessage: FC = ({ section }) => { {/* AddMessage */} - 확인 + 확인 diff --git a/src/pages/RevisePage.tsx b/src/pages/RevisePage.tsx index 8edef0a..35d86d6 100644 --- a/src/pages/RevisePage.tsx +++ b/src/pages/RevisePage.tsx @@ -27,7 +27,10 @@ const RetroRevisePage = () => { try { const data = await RetrospectiveService.onlyGet({ retrospectiveId: retrospectiveId }); setRetro(data.data); - setStatus(retro?.status); + console.log(retro); + if (retro) { + setStatus(retro.status.toString); + } } catch (e) { toast.error(e); } diff --git a/src/styles/writeRetroStyles/Layout.style.ts b/src/styles/writeRetroStyles/Layout.style.ts index 9cf5618..311d5ce 100644 --- a/src/styles/writeRetroStyles/Layout.style.ts +++ b/src/styles/writeRetroStyles/Layout.style.ts @@ -177,7 +177,7 @@ export const TaskText = styled.p` vertical-align: top; display: inline-block; margin: 20px 0; - margin-top: 5px; + margin-top: 20px; &:hover { cursor: pointer; } @@ -242,16 +242,12 @@ export const TaskMessageLine = styled.div` export const TaskMessageStyle = styled.div` min-height: 35px; - display: flex; margin-top: 10px; `; export const MessageUserProfile = styled.div``; -export const MessageTopStyle = styled.div` - display: flex; - position: relative; -`; +export const MessageTopStyle = styled.div``; export const MessageUserName = styled.p` font-size: 15px; From a20ebeee0bfd6270c9ea07e05ff26aae4ffca927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=B5=EC=A0=95=EB=AF=BC?= <102538362+jeongmin59@users.noreply.github.com> Date: Sun, 21 Apr 2024 00:01:51 +0900 Subject: [PATCH 2/8] =?UTF-8?q?:bug:=20Fix:=20=ED=9A=8C=EA=B3=A0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=BC=20=EB=82=A0=EC=A7=9C=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EC=99=84=EB=A3=8C=20(#193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/@types/Retrospectives.ts | 1 + src/api/@types/User.ts | 22 +++++++-------- src/api/axiosConfig.tsx | 2 +- src/components/RetroList/ContentsList.tsx | 28 +++++++++---------- .../createRetro/modal/ImageUpload.tsx | 23 +++++++++------ src/pages/MyPage.tsx | 2 +- src/pages/RetroListPage.tsx | 4 +++ 7 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/api/@types/Retrospectives.ts b/src/api/@types/Retrospectives.ts index d0da201..0514f96 100644 --- a/src/api/@types/Retrospectives.ts +++ b/src/api/@types/Retrospectives.ts @@ -46,6 +46,7 @@ export interface GetRetrospectiveResponseNodes { startDate: string; createdDate: string; updatedDate: string; + username: string; } export interface GetRetrospectiveData { diff --git a/src/api/@types/User.ts b/src/api/@types/User.ts index dff78c6..e070478 100644 --- a/src/api/@types/User.ts +++ b/src/api/@types/User.ts @@ -1,14 +1,14 @@ // get -export interface GetUsersRequest { - // userId: number; -} - export interface GetUsersResponse { - userId: number; - username: string; - email: string; - thumbnail: string | null; - phone: string | null; - createDate: Date; - updateDate: Date; + code: number; + data: { + userId: number; + userName: string; + email: string; + thumbnail: string | null; + phone: string | null; + createDate: Date; + updateDate: Date; + }; + message: string | null; } diff --git a/src/api/axiosConfig.tsx b/src/api/axiosConfig.tsx index e8d84d7..27b7b2b 100644 --- a/src/api/axiosConfig.tsx +++ b/src/api/axiosConfig.tsx @@ -65,7 +65,7 @@ axiosInstance.interceptors.request.use( // 헤더에 토큰 추가 config.headers.Authorization = `Bearer ${authToken}`; - console.log('헤더에 토큰 추가 확인', config.headers.Authorization); + // console.log('헤더에 토큰 추가 확인', config.headers.Authorization); return config; } catch (err) { console.error('에러', err); diff --git a/src/components/RetroList/ContentsList.tsx b/src/components/RetroList/ContentsList.tsx index d13c4ee..650107f 100644 --- a/src/components/RetroList/ContentsList.tsx +++ b/src/components/RetroList/ContentsList.tsx @@ -8,15 +8,12 @@ import { IoMdPerson } from 'react-icons/io'; import { MdPeople } from 'react-icons/md'; import { RxCounterClockwiseClock } from 'react-icons/rx'; //before import { useNavigate } from 'react-router-dom'; -import { useRecoilState } from 'recoil'; import { PatchRetrospectiveRequest } from '@/api/@types/Retrospectives'; import postImageToS3 from '@/api/imageApi/postImageToS3'; import { patchRetrospective } from '@/api/retrospectivesApi/patchRetrospective'; import Thumbnail from '@/assets/Thumbnail.png'; import Modal from '@/components/RetroList/Modal'; -import UserNickname from '@/components/user/UserNickname'; import { useCustomToast } from '@/hooks/useCustomToast'; -import { userNicknameState } from '@/recoil/user/userAtom'; import * as S from '@/styles/RetroList/ContentsList.styles'; interface Content { @@ -31,6 +28,7 @@ interface Content { startDate: string; createdDate: string; updatedDate: string; + username: string; } interface ContentListProps { @@ -42,7 +40,6 @@ interface ContentListProps { const ContentList: React.FC = ({ data, viewMode, searchData, setBookmarkUpdate }) => { // const [contentData, setContentData] = useState(data); 받아온데이터 - const [userNickname, setUserNickname] = useRecoilState(userNicknameState); const [openModalId, setOpenModalId] = useState(null); const toast = useCustomToast(); const [image, setImage] = useState<{ [key: number]: string }>({}); @@ -73,6 +70,12 @@ const ContentList: React.FC = ({ data, viewMode, searchData, s console.log('filter', filteredData); const navigate = useNavigate(); + const convertToLocalTime = (dateString: string | number | Date) => { + const date = new Date(dateString); + const localTime = new Date(date.getTime() - date.getTimezoneOffset() * 60000); + return localTime.toLocaleString(); // 로컬 타임존으로 변환하여 문자열로 반환 + }; + useEffect(() => { const fetchThumbnailsData = async (item: Content) => { try { @@ -138,15 +141,13 @@ const ContentList: React.FC = ({ data, viewMode, searchData, s }} />
- - {/* 생성자 이름(유저 식별 필요) */} - {userNickname} - + {item.username}
- {item.updatedDate && item.updatedDate !== item.startDate - ? `${item.updatedDate} 수정` - : item.startDate} + {item.updatedDate !== item.createdDate + ? `${convertToLocalTime(item.updatedDate)} 수정` + : convertToLocalTime(item.createdDate)} + {/* {item.updatedDate !== item.createdDate ? `${item.updatedDate} 수정` : item.createdDate} */} {item.status === 'NOT_STARTED' && ( = ({ data, viewMode, searchData, s navigate(`/section?retrospectiveId=${item.id}&teamId=${item.teamId}`)}> {item.title} - - {/* 생성자이름(유저 식별 필요) */} - {userNickname} - + {item.username} {item.updatedDate && item.updatedDate !== item.startDate ? `${item.updatedDate}` : item.startDate} diff --git a/src/components/createRetro/modal/ImageUpload.tsx b/src/components/createRetro/modal/ImageUpload.tsx index c5fbdb2..f8eafa9 100644 --- a/src/components/createRetro/modal/ImageUpload.tsx +++ b/src/components/createRetro/modal/ImageUpload.tsx @@ -45,16 +45,21 @@ const ImageUpload: React.FC = ({ onChange }) => { - {/* 이미지 미리보기 */} - {imagePreview && ( -
- Selected Image - -
- )} +
+ {imagePreview && ( + +
+ Selected Image +
+
+ +
+
+ )} +
); diff --git a/src/pages/MyPage.tsx b/src/pages/MyPage.tsx index bc1561c..a552b96 100644 --- a/src/pages/MyPage.tsx +++ b/src/pages/MyPage.tsx @@ -56,7 +56,7 @@ const MyPage = () => { - {userData?.thumbnail} + {userData?.data.thumbnail} diff --git a/src/pages/RetroListPage.tsx b/src/pages/RetroListPage.tsx index f04808d..9dd30f6 100644 --- a/src/pages/RetroListPage.tsx +++ b/src/pages/RetroListPage.tsx @@ -83,6 +83,7 @@ const RetroListPage = () => { startDate: formatDate(item.startDate), createdDate: formatDate(item.createdDate), updatedDate: formatDate(item.updatedDate), + username: item.username, })); setRetroData(rawData); }, [data.nodes]); @@ -104,6 +105,7 @@ const RetroListPage = () => { startDate: formatDate(item.startDate), createdDate: formatDate(item.createdDate), updatedDate: formatDate(item.updatedDate), + username: item.username, })); setRetroData(filtered); } else if (filterType === 'Teams') { @@ -121,6 +123,7 @@ const RetroListPage = () => { startDate: formatDate(item.startDate), createdDate: formatDate(item.createdDate), updatedDate: formatDate(item.updatedDate), + username: item.username, })); setRetroData(filtered); } else if (filterType === 'ALL') { @@ -136,6 +139,7 @@ const RetroListPage = () => { startDate: formatDate(item.startDate), createdDate: formatDate(item.createdDate), updatedDate: formatDate(item.updatedDate), + username: item.username, })); setRetroData(rawData); } From 7a51745f12405300aaa2bd00c7c001350ca675a3 Mon Sep 17 00:00:00 2001 From: h_h__jj <138123134+heejung0413@users.noreply.github.com> Date: Sun, 21 Apr 2024 01:09:30 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[Fix]api=20connection=20-=20=EC=A3=BC?= =?UTF-8?q?=EB=A1=9C=20revise=20=ED=8E=98=EC=9D=B4=EC=A7=80=20(#195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛Fix: revise 페이지 put api 연동 * 💄Design: 사이드바 수정 --- src/api/@types/Retrospectives.ts | 4 +- src/components/layout/parts/PageSideBar.tsx | 11 +++ .../writeRetro/revise/RetroImageUploader.tsx | 13 +-- .../writeRetro/revise/ReviseSetting.tsx | 88 ++++++++++--------- src/pages/RevisePage.tsx | 5 +- src/pages/SectionPage.tsx | 2 +- 6 files changed, 71 insertions(+), 52 deletions(-) diff --git a/src/api/@types/Retrospectives.ts b/src/api/@types/Retrospectives.ts index 0514f96..f32c722 100644 --- a/src/api/@types/Retrospectives.ts +++ b/src/api/@types/Retrospectives.ts @@ -90,7 +90,7 @@ export interface PutTeamRetrospectiveRequest { title: string; teamId?: number; description: string; - status: keyof TStatus; + status: string; thumbnail?: string; } @@ -98,7 +98,7 @@ export interface PutPersonalRetrospectiveRequest { retrospectiveId: number; title: string; description: string; - status: keyof TStatus; + status: string; thumbnail?: string; } diff --git a/src/components/layout/parts/PageSideBar.tsx b/src/components/layout/parts/PageSideBar.tsx index 3cb52ff..c9e0256 100644 --- a/src/components/layout/parts/PageSideBar.tsx +++ b/src/components/layout/parts/PageSideBar.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { PeopleFill, Person, PersonCircle, PersonFill, PlusCircleFill } from 'react-bootstrap-icons'; +import { IoArrowUndoSharp } from 'react-icons/io5'; import { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Flex } from '@chakra-ui/react'; import { useRecoilState } from 'recoil'; import Search_SideBar from './Search_SideBar'; @@ -92,6 +93,16 @@ const PageSideBar = () => { {/* Create New Retro */} + + + + + + Move to the List + + + + diff --git a/src/components/writeRetro/revise/RetroImageUploader.tsx b/src/components/writeRetro/revise/RetroImageUploader.tsx index 5510765..79deb90 100644 --- a/src/components/writeRetro/revise/RetroImageUploader.tsx +++ b/src/components/writeRetro/revise/RetroImageUploader.tsx @@ -4,11 +4,11 @@ import { Button, Image } from '@chakra-ui/react'; import { v4 as uuidv4 } from 'uuid'; interface Props { - image: string; - setImage: (image: File | null, uuid: string) => void; + image: string | undefined; + onChange: (image: File | null, uuid: string) => void; } -const RetroImageUploader: FC = ({ image, setImage }) => { +const RetroImageUploader: FC = ({ image, onChange }) => { const [preview, setPreview] = useState(null); const [_, setImageUUID] = useState(null); // 상태를 활용할 수 있도록 수정 @@ -22,8 +22,11 @@ const RetroImageUploader: FC = ({ image, setImage }) => { if (files) { const reader = new FileReader(); + // const imageUrl = URL.createObjectURL(files); + // const imageUrlString: string = imageUrl; const uuid = uuidv4(); - setImage(files, uuid); + // const fix_uuid = uuid.replace(/\n/gi, '\\n'); + onChange(files, uuid); reader.onloadend = () => { const result = reader.result as string; @@ -37,7 +40,7 @@ const RetroImageUploader: FC = ({ image, setImage }) => { const DeleteImage: MouseEventHandler = () => { setPreview(null); setImageUUID(null); - setImage(null, ''); + onChange(null, ''); }; return ( diff --git a/src/components/writeRetro/revise/ReviseSetting.tsx b/src/components/writeRetro/revise/ReviseSetting.tsx index 3512719..3405b9c 100644 --- a/src/components/writeRetro/revise/ReviseSetting.tsx +++ b/src/components/writeRetro/revise/ReviseSetting.tsx @@ -1,7 +1,6 @@ import { FC, useEffect, useState } from 'react'; import { BsPersonCircle } from 'react-icons/bs'; import { FaCheck } from 'react-icons/fa'; -import { IoIosInformationCircle } from 'react-icons/io'; import { IoClose } from 'react-icons/io5'; import { MdModeEdit } from 'react-icons/md'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -13,12 +12,14 @@ import { EditablePreview, Flex, FormControl, - FormLabel, IconButton, Input, - Switch, + Radio, + RadioGroup, + Stack, useEditableControls, } from '@chakra-ui/react'; +import axios from 'axios'; import DeleteRetrospective from './DeleteRetrospective'; import RetroImageUploader from './RetroImageUploader'; import { RetrospectiveData } from '@/api/@types/Retrospectives'; @@ -32,7 +33,7 @@ import * as S from '@/styles/writeRetroStyles/ReviseLayout.style'; interface Props { retro: RetrospectiveData; - status: string | undefined; + status: string; setStatus: (status: string) => void; } @@ -41,9 +42,8 @@ const ReviseSetting: FC = ({ retro, status, setStatus }) => { const query = search.split(/[=,&]/); const retrospectiveId = Number(query[1]); const teamId = Number(query[3]); - const [isChecked, setIsChecked] = useState(false); - - const [image, setImage] = useState(retro.thumbnail); + const [image, setImage] = useState(null); + const [imageURL, setImageURL] = useState(); const [title, setTitle] = useState(''); const [templateName, setTemplateName] = useState(); const [description, setDescription] = useState(''); @@ -54,7 +54,7 @@ const ReviseSetting: FC = ({ retro, status, setStatus }) => { if (retro) { try { const data = await postImageToS3({ filename: retro.thumbnail, method: 'GET' }); - setImage(data.data.preSignedUrl); + setImageURL(data.data.preSignedUrl); } catch (e) { toast.error(e); } @@ -76,47 +76,45 @@ const ReviseSetting: FC = ({ retro, status, setStatus }) => { try { if (teamId) { const data = await RetrospectiveService.putTeam({ - retrospectiveId: 102, + retrospectiveId: retro.retrospectiveId, title: title ?? retro.title, teamId: teamId, description: description ?? retro.description, - thumbnail: image ?? retro.thumbnail, - status: 'COMPLETED', + thumbnail: imageURL ?? retro.thumbnail, + status: status, }); console.log('put data', data); navigate('/retrolist'); toast.info('회고 수정이 정상 처리되었습니다.'); } else { const data = await RetrospectiveService.putPersonal({ - retrospectiveId: 102, + retrospectiveId: retro.retrospectiveId, title: title ?? retro.title, description: description ?? retro.description, - thumbnail: image ?? retro.thumbnail, - status: 'COMPLETED', + thumbnail: imageURL ?? retro.thumbnail, + status: status, }); console.log('put data', data); navigate('/retrolist'); toast.info('회고 수정이 정상 처리되었습니다.'); } - } catch (e) { - toast.error(e); - } - }; - const SwitchStatus = () => { - setIsChecked(true); - if (retro) { - if (isChecked) { - toast.info('회고 완료 처리를 취소하였습니다.'); - setStatus('COMPLETED'); - setIsChecked(false); - console.log(status); - } else { - toast.success('해당 회고는 최종 완료 처리되었습니다.'); - setStatus(retro.status); - console.log(status); - setIsChecked(true); + if (imageURL && retro.thumbnail !== imageURL) { + const response = await postImageToS3({ + filename: imageURL, + method: 'PUT', + }); + + const uploadResponse = await axios.put(response.data.preSignedUrl, image, { + headers: { + 'Content-Type': image?.type, + }, + }); + + console.log(uploadResponse.status); } + } catch { + toast.error('회고 수정이 정상 처리되지 않았습니다.'); } }; @@ -144,10 +142,14 @@ const ReviseSetting: FC = ({ retro, status, setStatus }) => { return ( +
+

회고 제목, 회고 상세 설명을 반드시 입력해야 회고 수정이 가능합니다 :)

+
{ - setImage(imageUUID); + image={imageURL} + onChange={(files, imageUUID) => { + imageUUID && setImageURL(imageUUID); + setImage(files); }} /> {/* 회고명 */} @@ -203,14 +205,18 @@ const ReviseSetting: FC = ({ retro, status, setStatus }) => { {/* 최종완료 */} - 회고 최종완료 + 회고 진행 단계 - - - - 회고가 최종 완료 단계로 처리되며 더이상 수정이 불가합니다. - - + + + + + 진행 전 + 진행 중 + 진행 완료 + + + {/* 회고 삭제 */} diff --git a/src/pages/RevisePage.tsx b/src/pages/RevisePage.tsx index 35d86d6..2738a72 100644 --- a/src/pages/RevisePage.tsx +++ b/src/pages/RevisePage.tsx @@ -20,16 +20,15 @@ const RetroRevisePage = () => { const teamId = Number(query[3]); const [retro, setRetro] = useState(); const [members, setMembers] = useState(); - const [status, setStatus] = useState(); + const [status, setStatus] = useState('NOT_STARTED'); const toast = useCustomToast(); const FetchRetrospective = async () => { try { const data = await RetrospectiveService.onlyGet({ retrospectiveId: retrospectiveId }); setRetro(data.data); - console.log(retro); if (retro) { - setStatus(retro.status.toString); + setStatus(retro.status); } } catch (e) { toast.error(e); diff --git a/src/pages/SectionPage.tsx b/src/pages/SectionPage.tsx index f9e2229..4327402 100644 --- a/src/pages/SectionPage.tsx +++ b/src/pages/SectionPage.tsx @@ -67,7 +67,7 @@ const RetroTeamPage = () => { fetchSection(); fetchRetrospective(); fetchTemplate(); - }, [retro?.status, template?.values, section]); + }, [retro?.status, template?.values]); return ( From 8d18af4e0de810ceff2bcff84af4973f453977d8 Mon Sep 17 00:00:00 2001 From: Yena Kim <76564438+yeneua@users.noreply.github.com> Date: Sun, 21 Apr 2024 01:16:14 +0900 Subject: [PATCH 4/8] =?UTF-8?q?:lipstick:=20Design:=20RetroList=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95=20(#196)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/BookmarkIcon_N.png | Bin 579 -> 0 bytes src/assets/BookmarkIcon_Y.png | Bin 1163 -> 0 bytes src/assets/Link.png | Bin 1162 -> 0 bytes src/assets/TeamIcon.png | Bin 1333 -> 0 bytes src/assets/UserProfile2.png | Bin 12220 -> 0 bytes src/components/RetroList/ContentsList.tsx | 26 +++++++++++--------- src/styles/RetroList/ContentsList.styles.ts | 20 +++++++++++++++ 7 files changed, 35 insertions(+), 11 deletions(-) delete mode 100644 src/assets/BookmarkIcon_N.png delete mode 100644 src/assets/BookmarkIcon_Y.png delete mode 100644 src/assets/Link.png delete mode 100644 src/assets/TeamIcon.png delete mode 100644 src/assets/UserProfile2.png diff --git a/src/assets/BookmarkIcon_N.png b/src/assets/BookmarkIcon_N.png deleted file mode 100644 index 176bef53f31dbad22e6f22b9e52e7b65d95e99a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 579 zcmV-J0=)f+P)mS#$kg#t6WhmN&&u?=scD=key z;ZIv$b@V;~KIAdHiK!CGi8u~c3HPxOnA5QBH{!9aiDh+F~K#ET%4fRFbQb zL{tr00FK17OV8tVFdYjRGwq*U9(ekhl2_uSY8J$*$j;menGsUy)+&gZ)BU^MRP3-Q zlH`Qq+uFVsr~>pMGzWSAqNmSYTV8*bcgEMViyIPiBY?IYEqQ0u4Z$HG_o$h_->AAn zE+39#KH5ZtbZ@F9`ZX%Ad#i4a%BvpfC*eb^^&5$tp9G?id$fivhXomL{dyqTvx=XO zM#G}XJ@XmIV$hXc0&<}E+x65lGLw<=S9al|&MVUW?POlPqta!E@RXUF>xrq;C#xl< ztBn{m@1e-XeEOm?+B)cem{QGNyIr?g=`N18Dr=+%l79d4hZgy_hl3XhwaLP96!I&u zQ#@pUt1|B}fbn-ql6moSJuy1X+-Z#K`9-0=yw|bQ3-PI`PgecuRm@92{s$Z0-kcTW Rx-0+y002ovPDHLkV1lH31q%QG diff --git a/src/assets/BookmarkIcon_Y.png b/src/assets/BookmarkIcon_Y.png deleted file mode 100644 index 786cd9faf864716bd00ef0cb2ab0217a1337e77b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1163 zcmV;61a$j}P)@~0drDELIAGL9O(c600d`2O+f$vv5yPeH%_`5+lGF33ZGHXj~-O!zmpON;MWZ5u5l3#{_5)`3s*=Rv>->4&^Nl z;h4spL;^)fa1DL>xI;&D=;(;%oRr2O6GE)IbyFiws^Fp=r*3M-N%z=CHJrMs87KAF zMG>mu%uUXnB!BYLEJAfctZ6(ME8yHoHMuA!7F+1RGESXTqla6=x#xJ1#A}>-0ZuyUCrCgvB6h*-cIYcMXHbViN3$9bCc!)}53I^cA)VaLVVh zfK?}r2o%8$D(|#ccY@A}lT?kFeIbge0dEo3GKFP#4d^>bF3@7HK{=6Dq01_z8WPw*WjoKg zdJ$yl47DXApj^7(_z|IQM7dOtS&qBM8OgdFm8OCRIGI+~;chXYszy-j=D*SdO4D)D zCF($ZHc~)E!5zvu&#HS-CQ-FfX;)PSLOooRZD{7CbZf;GOi^7*;@EVjV=bk3jXbwm zwIp4hWAZshYFm}suTg0$C_8J5UG*L&dG=(uxu(?lPyuyD2zKbT!*SEM}>_O dcJL82_y;iGNLFRm!Epcp002ovPDHLkV1i%S3P1n= diff --git a/src/assets/Link.png b/src/assets/Link.png deleted file mode 100644 index 45aa0fb832cf73221bdb136b1c0865cc15a29a6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1162 zcmV;51a@~0drDELIAGL9O(c600d`2O+f$vv5yP)h6NTc7v;pY2OM*?Qdn zy|;C@FB%zR>($!hI`DWBgPb_G9I79fDRIjE6kRwl`fIkd(6Yq^LdZ9zV82)aPN4WyDuGl zcI->G%$9-sGtmLAu5E}g0rybA4WkYf#usMG)pJk>-LD&4m_Xq`=Qit4XX+VSA8kF@ z`hjM4-ca<+imO}Y%*zQeINmxpNbCbhX-m>`pJMBetu>k!-j{2)WXRrQwz|%cT%o@j zJf2&W>vWevJb`WiM`H?ehnb(y4hd5iTIfF8*utzZW1(RR!Z6H&80L&H z%o$;rGcxrs&%iMM@x6FwtYNAO4D*>F?@o7#Fx46i^Vu5H57YO1RG2w~d4#D9PCQJd zLFEWj;NOj>{5hE$oN$;1dBRja7t^K=vsIxv>8_uBg%i3Qa1HkYUm(sJ!-RRVbf3+& zz*mTO1BO}Dcen!;#4})+MY&o~jsBKkn8j#d#{!L+Nq2~brz?+k-%D_gYc^XP57dCN z$#E&$&hhvs41bB=0!<6o3zP0_{RDM#Z4eDfaJUD#26Y;1pn~JW!2)!U>lmw`Jb`;` zuQ3BTey7MaJ_^bbG+~M3d9&C`&JpN?<8gy>1b$(v6jyA|(h}q7+5}$NeIf^ ztCL|C6f!ZIVNOJ;yXv;3D^LZ>N>J`r$(%4&&=UE=yagwW4MO05b(&Ar-~_Z$5H!JR zHyJZdYKt_WXJh*Td$X@EXu3_oOZWM-FFg_c3(ZWu2k>XUiHL}Zh=_=Yh=_=Yh=_=Y ch=@A<1@787xD>iKkpKVy07*qoM6N<$f<~|yv;Y7A diff --git a/src/assets/TeamIcon.png b/src/assets/TeamIcon.png deleted file mode 100644 index 6a00cfeea04d7d477a15db9738b2dfd8380ffdc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1333 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 KMNUMnLSTY|Dzsn# diff --git a/src/components/RetroList/ContentsList.tsx b/src/components/RetroList/ContentsList.tsx index 650107f..b52b03c 100644 --- a/src/components/RetroList/ContentsList.tsx +++ b/src/components/RetroList/ContentsList.tsx @@ -1,9 +1,6 @@ import { useEffect, useState } from 'react'; import { CgTimelapse } from 'react-icons/cg'; // ing -import { CiStar } from 'react-icons/ci'; -import { FaStar } from 'react-icons/fa'; import { FaRegCircleCheck } from 'react-icons/fa6'; // done -import { HiOutlineDotsHorizontal } from 'react-icons/hi'; import { IoMdPerson } from 'react-icons/io'; import { MdPeople } from 'react-icons/md'; import { RxCounterClockwiseClock } from 'react-icons/rx'; //before @@ -73,7 +70,14 @@ const ContentList: React.FC = ({ data, viewMode, searchData, s const convertToLocalTime = (dateString: string | number | Date) => { const date = new Date(dateString); const localTime = new Date(date.getTime() - date.getTimezoneOffset() * 60000); - return localTime.toLocaleString(); // 로컬 타임존으로 변환하여 문자열로 반환 + const options: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }; + return localTime.toLocaleString(undefined, options); // 로컬 타임존으로 변환하여 문자열로 반환 }; useEffect(() => { @@ -124,10 +128,10 @@ const ContentList: React.FC = ({ data, viewMode, searchData, s }} > {item.isBookmarked && ( - handleBookmark(item.id)} style={{ color: '#fcea12' }} size="19" /> + handleBookmark(item.id)} style={{ color: '#fcea12' }} size="19" /> )} - {!item.isBookmarked && handleBookmark(item.id)} size={20} />} - handleBookmark(item.id)} size={20} />} + openModalForItem(item.id)} @@ -190,13 +194,13 @@ const ContentList: React.FC = ({ data, viewMode, searchData, s {item.isBookmarked && ( - handleBookmark(item.id)} style={{ color: '#fcea12' }} size="19" /> + handleBookmark(item.id)} style={{ color: '#fcea12' }} size="19" /> )} - {!item.isBookmarked && handleBookmark(item.id)} size={20} />} + {!item.isBookmarked && handleBookmark(item.id)} size={20} />} {item.status === 'NOT_STARTED' && ( - @@ -212,7 +216,7 @@ const ContentList: React.FC = ({ data, viewMode, searchData, s )} - openModalForItem(item.id)} diff --git a/src/styles/RetroList/ContentsList.styles.ts b/src/styles/RetroList/ContentsList.styles.ts index 0d2be60..c8a7345 100644 --- a/src/styles/RetroList/ContentsList.styles.ts +++ b/src/styles/RetroList/ContentsList.styles.ts @@ -1,3 +1,6 @@ +import { CiStar } from 'react-icons/ci'; +import { FaStar } from 'react-icons/fa'; +import { HiOutlineDotsHorizontal } from 'react-icons/hi'; import styled from 'styled-components'; export const BoardContainer = styled.div` @@ -134,3 +137,20 @@ export const Icon = styled.img` cursor: pointer; } `; +export const StyledCiStar = styled(CiStar)` + &:hover { + cursor: pointer; + } +`; + +export const StyledHiOutlineDotsHorizontal = styled(HiOutlineDotsHorizontal)` + &:hover { + cursor: pointer; + } +`; + +export const StyledFaStar = styled(FaStar)` + &:hover { + cursor: pointer; + } +`; From d89b5046df48470838fdc38ddbf8ded6646cc261 Mon Sep 17 00:00:00 2001 From: Yena Kim <76564438+yeneua@users.noreply.github.com> Date: Sun, 21 Apr 2024 03:32:52 +0900 Subject: [PATCH 5/8] =?UTF-8?q?:sparkles:=20Feat:=20Action=20Items=20?= =?UTF-8?q?=EB=8B=B4=EB=8B=B9=EC=9E=90=20=EC=A7=80=EC=A0=95=20api=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C=20(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: Feat: Action Items 담당자 지정 api 타입 작성 및 비동기 함수 작성 * :bug: Fix: Action Items api 오타 수정 및 절대 경로로 수정 * :sparkles: Feat: Action Items 팀원 get api 연결 * :sparkles: Feat: Action Items 담당자 지정 api 연결 * :sparkles: Feat: Action Items 담당자 지정 api 구현 완료 --- src/api/@types/TeamController.tsx | 14 +++++ src/api/services/TeamController.ts | 14 ++++- src/api/teamControllerApi/getMember.tsx | 15 +++++ .../putActionItemsMember.tsx | 13 ++++ .../{task => ActionItems}/ActionItemTask.tsx | 59 +++++++++++++------ .../writeRetro/ActionItems/Members.tsx | 35 +++++++++-- src/components/writeRetro/task/TeamTask.tsx | 15 ++++- src/mocks/handlers.ts | 8 +++ yarn.lock | 3 +- 9 files changed, 151 insertions(+), 25 deletions(-) create mode 100644 src/api/teamControllerApi/getMember.tsx create mode 100644 src/api/teamControllerApi/putActionItemsMember.tsx rename src/components/writeRetro/{task => ActionItems}/ActionItemTask.tsx (50%) diff --git a/src/api/@types/TeamController.tsx b/src/api/@types/TeamController.tsx index 1389bfa..096d54f 100644 --- a/src/api/@types/TeamController.tsx +++ b/src/api/@types/TeamController.tsx @@ -1,3 +1,4 @@ +// 팀원 조회 export interface GetTeamMembersRequest { teamId: number; retrospectiveId: number; @@ -35,7 +36,20 @@ export interface TemplateNameData { sequence: number; } +// put 담당자 +export interface PutActionItemsRequest { + teamId: number; + retrospectiveId: number; + sectionId: number; +} + +export interface PutActionItemsResponse { + code: number; + message: string; + data: object; +} export interface TeamControllerClient { TeamMemberGet(request: GetTeamMembersRequest): Promise; TemplateNameGet(request: GetTemplateNameRequest): Promise; + ActionItemsMemberPut(request: PutActionItemsRequest): Promise; } diff --git a/src/api/services/TeamController.ts b/src/api/services/TeamController.ts index 978a7cd..e40f33c 100644 --- a/src/api/services/TeamController.ts +++ b/src/api/services/TeamController.ts @@ -3,8 +3,10 @@ import { GetTemplateNameRequest, GetTemplateNameResponse, TeamControllerClient, -} from '../@types/TeamController'; -import axiosInstance from '../axiosConfig'; + PutActionItemsRequest, + PutActionItemsResponse, +} from '@/api/@types/TeamController'; +import axiosInstance from '@/api/axiosConfig'; const TEAMS_ROUTE = 'teams'; const TEMPLATE_ROUTE = 'retrospective-templates'; @@ -26,4 +28,12 @@ export const TeamControllerServices: TeamControllerClient = { throw new Error(error as string); } }, + ActionItemsMemberPut: async (request: PutActionItemsRequest): Promise => { + try { + const response = await axiosInstance.put(`/sections/action-itmes`, request); + return response.data; + } catch (error) { + throw new Error(error as string); + } + }, }; diff --git a/src/api/teamControllerApi/getMember.tsx b/src/api/teamControllerApi/getMember.tsx new file mode 100644 index 0000000..c946fdf --- /dev/null +++ b/src/api/teamControllerApi/getMember.tsx @@ -0,0 +1,15 @@ +import { GetTeamMembersRequest, GetTeamMembersResponse } from '@/api/@types/TeamController'; +import axiosInstance from '@/api/axiosConfig'; + +export const getMember = async ({ + teamId, + retrospectiveId, +}: GetTeamMembersRequest): Promise => { + try { + const response = await axiosInstance.get(`/teams/${teamId}/users?${retrospectiveId}=`); + console.log('팀 멤버 조회 성공', response.data); + return response.data; + } catch (error) { + throw new Error('팀 멤버 조회 실패'); + } +}; diff --git a/src/api/teamControllerApi/putActionItemsMember.tsx b/src/api/teamControllerApi/putActionItemsMember.tsx new file mode 100644 index 0000000..dccf514 --- /dev/null +++ b/src/api/teamControllerApi/putActionItemsMember.tsx @@ -0,0 +1,13 @@ +import { PutActionItemsRequest, PutActionItemsResponse } from '@/api/@types/TeamController'; +import axiosInstance from '@/api/axiosConfig'; + +export const putActionItemsMember = async (requestData: PutActionItemsRequest): Promise => { + try { + // console.log(requestData); + const response = await axiosInstance.put('/sections/action-items', requestData); + // console.log('action item 멤버 저장 성공', response); + return response.data; + } catch (error) { + throw new Error('action item 멤버 저장 실패'); + } +}; diff --git a/src/components/writeRetro/task/ActionItemTask.tsx b/src/components/writeRetro/ActionItems/ActionItemTask.tsx similarity index 50% rename from src/components/writeRetro/task/ActionItemTask.tsx rename to src/components/writeRetro/ActionItems/ActionItemTask.tsx index 1ac2cfd..9e5769b 100644 --- a/src/components/writeRetro/task/ActionItemTask.tsx +++ b/src/components/writeRetro/ActionItems/ActionItemTask.tsx @@ -1,25 +1,46 @@ -import React, { FC, useState } from 'react'; -import { sectionData } from '@/api/@types/Section'; -import UserProfile1 from '@/assets/UserProfile1.png'; -import UserProfile2 from '@/assets/UserProfile2.png'; +import React, { FC, useState, useEffect } from 'react'; +import { TeamControllerServices } from '@/api/services/TeamController'; import Members from '@/components/writeRetro/ActionItems/Members'; +import { useCustomToast } from '@/hooks/useCustomToast'; import * as S from '@/styles/writeRetroStyles/Layout.style'; -interface Props { - section: sectionData[]; +interface ActionItemTaskProps { + tId: number; + rId: number; + sId: number; } -const ActionItemTask: FC = ({ section }) => { - // action items 담당자 지정 +const ActionItemTask: FC = ({ tId, rId, sId }) => { const [hoveredUser, setHoveredUser] = useState(null); const [showPopup, setShowPopup] = useState(false); const [selectedUserName, setSelectedUserName] = useState(null); const [selectedUserImg, setSelectedUserImg] = useState(null); - const users = [ - { name: 'User 1', image: UserProfile1 }, - { name: 'User 2', image: UserProfile2 }, - ]; + const teamId: number = tId; + const retrospectiveId: number = rId; + const sectionId: number = sId; + + const [users, setUsers] = useState<{ name: string; image: string }[]>([]); + const toast = useCustomToast(); + + const fetchTeamMember = async () => { + try { + if (teamId) { + const data = await TeamControllerServices.TeamMemberGet({ teamId: teamId, retrospectiveId: retrospectiveId }); + const userData = data.data.map(member => ({ + name: member.username, + image: member.profileImage, + })); + setUsers(userData); + } + return; + } catch (e) { + toast.error('멤버 조회 실패'); + } + }; + useEffect(() => { + fetchTeamMember(); + }, []); const togglePopup = () => { setShowPopup(!showPopup); @@ -50,15 +71,19 @@ const ActionItemTask: FC = ({ section }) => { onMouseLeave={handleMouseLeave} > {selectedUserImg ? : 'M'} - {hoveredUser && {hoveredUser}} {/* 이름 : {name.username} */} + {hoveredUser && {hoveredUser}} {showPopup && ( - + )}
- {section.map(section => ( - {section.username} - ))} ); }; diff --git a/src/components/writeRetro/ActionItems/Members.tsx b/src/components/writeRetro/ActionItems/Members.tsx index 67e9546..56d052f 100644 --- a/src/components/writeRetro/ActionItems/Members.tsx +++ b/src/components/writeRetro/ActionItems/Members.tsx @@ -1,15 +1,42 @@ +import { PutActionItemsRequest } from '@/api/@types/TeamController'; +import { putActionItemsMember } from '@/api/teamControllerApi/putActionItemsMember'; +import UserProfile from '@/assets/UserProfile1.png'; +import { useCustomToast } from '@/hooks/useCustomToast'; import * as S from '@/styles/writeRetroStyles/Members.styles'; interface UserListProps { users: { name: string; image: string }[]; onSelectUserImg: (image: string) => void; onSelectUserName: (name: string) => void; + tId: number; + rId: number; + sId: number; } -export const Members: React.FC = ({ users, onSelectUserImg, onSelectUserName }) => { - const handleUserClick = (name: string, image: string) => { +export const Members: React.FC = ({ users, onSelectUserImg, onSelectUserName, tId, rId, sId }) => { + const teamId: number = tId; + const retrospectiveId: number = rId; + const sectionId: number = sId; + + const toast = useCustomToast(); + + const putActionItemMember = async () => { + try { + const requestData: PutActionItemsRequest = { + teamId: teamId, + retrospectiveId: retrospectiveId, + sectionId: sectionId, + }; + await putActionItemsMember(requestData); + } catch (e) { + toast.error('담당자 지정 실패'); + } + }; + + const handleUserClick = async (name: string, image: string) => { onSelectUserName(name); onSelectUserImg(image); + await putActionItemMember(); }; return ( @@ -20,8 +47,8 @@ export const Members: React.FC = ({ users, onSelectUserImg, onSel
    {users.map((user, index) => ( - handleUserClick(user.name, user.image)}> - {user.name} + handleUserClick(user.name, user.image || UserProfile)}> + {user.name} ))}
diff --git a/src/components/writeRetro/task/TeamTask.tsx b/src/components/writeRetro/task/TeamTask.tsx index 2f6560d..edf61af 100644 --- a/src/components/writeRetro/task/TeamTask.tsx +++ b/src/components/writeRetro/task/TeamTask.tsx @@ -3,6 +3,7 @@ import { BiLike, BiSolidLike } from 'react-icons/bi'; import { CgProfile } from 'react-icons/cg'; import { FaRegTrashAlt } from 'react-icons/fa'; import { MdAccessAlarm, MdMessage } from 'react-icons/md'; +import { useLocation } from 'react-router-dom'; import { Button, Flex, @@ -19,7 +20,7 @@ import { formattedDate } from './PersonalTask'; import TeamTaskMessage from './taskMessage/TeamTaskMessage'; import { sectionData } from '@/api/@types/Section'; import { SectionServices } from '@/api/services/Section'; - +import ActionItemTask from '@/components/writeRetro/ActionItems/ActionItemTask'; import ReviseModal from '@/components/writeRetro/task/ReviseModal'; import { useCustomToast } from '@/hooks/useCustomToast'; import * as S from '@/styles/writeRetroStyles/Layout.style'; @@ -33,6 +34,12 @@ const TeamTask: FC = ({ section }) => { const [liked, setLiked] = useState(0); const [messaged, setMessaged] = useState(false); const [isVisible, setIsVisible] = useState(false); + const { search } = useLocation(); + + const query = search.split(/[=,&]/); + const rId = Number(query[1]); // action-items로 넘겨줄 Id값들 + const tId = Number(query[3]); + const sId: number = section.sectionId; const handleLike = async () => { try { @@ -100,6 +107,12 @@ const TeamTask: FC = ({ section }) => { {section.content} {/* (수정됨) */} + +
+ +
+ 담당자 +
diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 65d120e..68b7534 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -64,6 +64,14 @@ export const SectionHandlers: RequestHandler[] = [ }; return HttpResponse.json(mockLikes); }), + http.put(`${SECTION_ROUTE}/action-items`, () => { + const mockActionItems = { + code: 0, + message: 'string', + data: {}, + }; + return HttpResponse.json(mockActionItems); + }), ]; //teamMembers diff --git a/yarn.lock b/yarn.lock index 10c65c3..e0916ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9620,7 +9620,7 @@ react-remove-scroll@^2.5.6: react-router-dom@^6.22.3: version "6.22.3" - resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.3.tgz#9781415667fd1361a475146c5826d9f16752a691" integrity sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw== dependencies: "@remix-run/router" "1.15.3" @@ -11469,6 +11469,7 @@ wordwrap@~0.0.2: integrity sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 92ce537ca2acb1a421962c1300c516cd184cbefc Mon Sep 17 00:00:00 2001 From: sunflower888 <84950772+sunflower888@users.noreply.github.com> Date: Sun, 21 Apr 2024 03:54:40 +0900 Subject: [PATCH 6/8] =?UTF-8?q?:sparkles:=20Feat=20:=20Survey=20API=20?= =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EC=88=98=EC=A0=95=20(#198)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: Feat : Survey API 연결 * :sparkles: Feat : Survey API 최종 수정 --------- Co-authored-by: geumbin --- src/api/@types/Survey.ts | 3 +- src/api/axiosConfig.tsx | 2 +- src/api/survey/postSurvey.tsx | 2 +- src/components/survey/GenderRadio.tsx | 10 +++-- src/components/survey/PurposeCheckbox.tsx | 42 +++++++++++++------ src/pages/SurveyPage.tsx | 50 ++++++++++++----------- 6 files changed, 67 insertions(+), 42 deletions(-) diff --git a/src/api/@types/Survey.ts b/src/api/@types/Survey.ts index c5c02db..ee816f5 100644 --- a/src/api/@types/Survey.ts +++ b/src/api/@types/Survey.ts @@ -5,7 +5,8 @@ export interface PostSurveyRequest { occupation: string; region: string; source: string; - purpose: string; + purposes: string[] | undefined; + } export interface PostSurveyResponse { diff --git a/src/api/axiosConfig.tsx b/src/api/axiosConfig.tsx index 27b7b2b..e8d84d7 100644 --- a/src/api/axiosConfig.tsx +++ b/src/api/axiosConfig.tsx @@ -65,7 +65,7 @@ axiosInstance.interceptors.request.use( // 헤더에 토큰 추가 config.headers.Authorization = `Bearer ${authToken}`; - // console.log('헤더에 토큰 추가 확인', config.headers.Authorization); + console.log('헤더에 토큰 추가 확인', config.headers.Authorization); return config; } catch (err) { console.error('에러', err); diff --git a/src/api/survey/postSurvey.tsx b/src/api/survey/postSurvey.tsx index ace0538..79f11cb 100644 --- a/src/api/survey/postSurvey.tsx +++ b/src/api/survey/postSurvey.tsx @@ -4,7 +4,7 @@ import axiosInstance from '../axiosConfig'; // post 요청 export const PostSurvey = async (requestData: PostSurveyRequest): Promise => { try { - const response = await axiosInstance.post('/surveys/response', requestData); + const response = await axiosInstance.post('/surveys/responses', requestData); console.log('설문조사 전송 성공', response.data); return response.data; } catch (error) { diff --git a/src/components/survey/GenderRadio.tsx b/src/components/survey/GenderRadio.tsx index 88958ab..a8093a7 100644 --- a/src/components/survey/GenderRadio.tsx +++ b/src/components/survey/GenderRadio.tsx @@ -7,7 +7,9 @@ interface Gender { } const GenderRadio: React.FC = ({ onGenderChange }) => { - const [gender, setGender] = useState('female'); + + const [gender, setGender] = useState('FEMALE'); + const handleGenderChange = (event: React.ChangeEvent) => { const newGender = event.target.value; setGender(newGender); @@ -21,10 +23,12 @@ const GenderRadio: React.FC = ({ onGenderChange }) => { - + + 여성 - + + 남성 diff --git a/src/components/survey/PurposeCheckbox.tsx b/src/components/survey/PurposeCheckbox.tsx index bf76002..730740b 100644 --- a/src/components/survey/PurposeCheckbox.tsx +++ b/src/components/survey/PurposeCheckbox.tsx @@ -1,15 +1,28 @@ -import React, { useState } from 'react'; + +import React, { useState, useEffect } from 'react'; + import { Checkbox, Stack, Input, Text } from '@chakra-ui/react'; import * as S from '@/styles/survey/PurposeCheckbox.style'; interface Purpose { - onPurposeChange: (purpose: string) => void; - onOtherPurposeChange: (otherPurpose: string) => void; - // onPurposeChange: (purpose: string[]) => void; + + onPurposeChange: (purpose: string[]) => void; } -const PurposeCheckbox: React.FC = ({ onPurposeChange, onOtherPurposeChange }) => { +const PurposeCheckbox: React.FC = ({ onPurposeChange }) => { const [checkedPurposes, setCheckedPurposes] = useState([]); + const [otherPurpose, setOtherPurpose] = useState(''); + const [isOtherSelected, setIsOtherSelected] = useState(false); + + useEffect(() => { + const updatedPurposes = [...checkedPurposes]; + if (isOtherSelected) { + updatedPurposes.push(otherPurpose); + } + onPurposeChange(updatedPurposes); + }, [checkedPurposes, otherPurpose, isOtherSelected, onPurposeChange]); + + // purposes const handlePurposeChange = (event: React.ChangeEvent) => { const isChecked = event.target.checked; @@ -17,20 +30,21 @@ const PurposeCheckbox: React.FC = ({ onPurposeChange, onOtherPurposeCha let updatedPurposes: string[]; if (isChecked) { - updatedPurposes = [...checkedPurposes, purpose]; // 체크된 경우 배열에 추가 + + updatedPurposes = [...checkedPurposes, purpose]; } else { - updatedPurposes = checkedPurposes.filter(item => item !== purpose); // 체크 해제된 경우 배열에서 제거 + updatedPurposes = checkedPurposes.filter(item => item !== purpose); } - setCheckedPurposes(updatedPurposes); // 최종 업데이트 한번만 수행 - onPurposeChange(updatedPurposes.join(', ')); // 선택된 목적들을 문자열로 변환하여 전달 + setCheckedPurposes(updatedPurposes); }; - const [otherPurpose, setOtherPurpose] = useState(''); + // otherPurpose const handleOtherPurposeChange = (event: React.ChangeEvent) => { const newOtherPurpose = event.target.value; setOtherPurpose(newOtherPurpose); - onOtherPurposeChange(newOtherPurpose); + setIsOtherSelected(!!newOtherPurpose); + }; return ( @@ -58,7 +72,11 @@ const PurposeCheckbox: React.FC = ({ onPurposeChange, onOtherPurposeCha - 기타 + + + 기타 + + { localStorage.setItem('surveyVisited', 'true'); }, []); + + const navigate = useNavigate(); + const handleSurveyButtonClick = () => { handleSurvey(); }; const handleSurvey = async () => { try { - // + path 기타 + purpose 기타 + purpose 복수 답안 + 직업 value + console.log( '나이는:', age, @@ -34,36 +39,31 @@ const SurveyPage: React.FC = () => { path, '/목적은(복수선택):', purpose, - '/기타 목적은:', - otherPurpose, - ); - // const SurveyRequest = await PostSurvey({ - // age: age, - // gender: gender, - // occupation: job, - // region: city, - // source: path, - // purpose: purpose, - // otherPurpose: otherPurpose - // }); - // console.log('설문조사 전송 성공', SurveyRequest); - // alert('설문조사가 전송되었습니다.'); + + console.log('설문조사 전송 성공', SurveyRequest); + alert('설문조사가 전송되었습니다.'); + navigate('/'); + } catch (error) { console.error('실패입니다.', error); } }; const [age, setAge] = useState(''); - const [gender, setGender] = useState('female'); + + const [gender, setGender] = useState('FEMALE'); const [job, setJob] = useState(''); const [city, setCity] = useState('서울'); const [path, setPath] = useState(''); - const [purpose, setPurpose] = useState(); - const [otherPurpose, setOtherPurpose] = useState(); + const [purpose, setPurpose] = useState(); + const handleAgeChange = (age: string) => { setAge(age); }; + + const numAge: number = parseInt(age, 10); + const handleGenderChange = (gender: string) => { setGender(gender); }; @@ -76,12 +76,12 @@ const SurveyPage: React.FC = () => { const handlePathChange = (path: string) => { setPath(path); }; - const handlePurposeChange = (purpose: string) => { + + const handlePurposeChange = (purpose: string[]) => { setPurpose(purpose); }; - const handleOtherPurposeChange = (otherPurpose: string) => { - setOtherPurpose(otherPurpose); - }; + + return ( <> @@ -99,7 +99,9 @@ const SurveyPage: React.FC = () => { - + + + From 850ba8518d6ca801f50aa0590bd3f9ec10bcbd49 Mon Sep 17 00:00:00 2001 From: sunflower888 <84950772+sunflower888@users.noreply.github.com> Date: Sun, 21 Apr 2024 04:46:09 +0900 Subject: [PATCH 7/8] =?UTF-8?q?:sparkles:=20Feat=20:=20Survey=20API=20?= =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EC=88=98=EC=A0=95=20(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: geumbin --- src/components/survey/GenderRadio.tsx | 3 -- src/components/survey/PurposeCheckbox.tsx | 46 ----------------------- src/pages/SurveyPage.tsx | 22 +++++------ 3 files changed, 9 insertions(+), 62 deletions(-) diff --git a/src/components/survey/GenderRadio.tsx b/src/components/survey/GenderRadio.tsx index a8093a7..5287be8 100644 --- a/src/components/survey/GenderRadio.tsx +++ b/src/components/survey/GenderRadio.tsx @@ -7,7 +7,6 @@ interface Gender { } const GenderRadio: React.FC = ({ onGenderChange }) => { - const [gender, setGender] = useState('FEMALE'); const handleGenderChange = (event: React.ChangeEvent) => { @@ -23,12 +22,10 @@ const GenderRadio: React.FC = ({ onGenderChange }) => { - 여성 - 남성 diff --git a/src/components/survey/PurposeCheckbox.tsx b/src/components/survey/PurposeCheckbox.tsx index 730740b..4ff00a0 100644 --- a/src/components/survey/PurposeCheckbox.tsx +++ b/src/components/survey/PurposeCheckbox.tsx @@ -1,11 +1,9 @@ - import React, { useState, useEffect } from 'react'; import { Checkbox, Stack, Input, Text } from '@chakra-ui/react'; import * as S from '@/styles/survey/PurposeCheckbox.style'; interface Purpose { - onPurposeChange: (purpose: string[]) => void; } @@ -30,7 +28,6 @@ const PurposeCheckbox: React.FC = ({ onPurposeChange }) => { let updatedPurposes: string[]; if (isChecked) { - updatedPurposes = [...checkedPurposes, purpose]; } else { updatedPurposes = checkedPurposes.filter(item => item !== purpose); @@ -44,7 +41,6 @@ const PurposeCheckbox: React.FC = ({ onPurposeChange }) => { const newOtherPurpose = event.target.value; setOtherPurpose(newOtherPurpose); setIsOtherSelected(!!newOtherPurpose); - }; return ( @@ -72,7 +68,6 @@ const PurposeCheckbox: React.FC = ({ onPurposeChange }) => { - 기타 @@ -92,44 +87,3 @@ const PurposeCheckbox: React.FC = ({ onPurposeChange }) => { }; export default PurposeCheckbox; - -// import React from 'react'; -// import { Checkbox, Stack, Input, Text } from '@chakra-ui/react'; -// import * as S from '@/styles/survey/PurposeCheckbox.style'; - -// interface Purpose { -// onPurposeChange: (city: string) => void; -// } - -// const PurposeCheckbox: React.FC = ({ onPurposeChange }) => { -// const [purpose, setPurpose] = useState('서울'); -// const handlePurposeChange = (event: React.ChangeEvent) => { -// const newPurpose = event.target.value; -// setPurpose(newPurpose); -// onPurposeChange(newPurpose); -// }; - -// return ( -// <> -// -// Past-Forward 서비스를 알게 된 경로는 무엇입니까? -// (복수 선택 가능) -// -// -// -// 업무 목적 -// 개인 발전 -// 팀 협업 -// 프로젝트 관리 -// 학습 및 개선 -// -// -// 기타 -// -// -// -// -// -// -// ); -// }; diff --git a/src/pages/SurveyPage.tsx b/src/pages/SurveyPage.tsx index 493287d..4cf9f43 100644 --- a/src/pages/SurveyPage.tsx +++ b/src/pages/SurveyPage.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect } from 'react'; - import { useNavigate } from 'react-router-dom'; import { Text, Button, Divider } from '@chakra-ui/react'; import { PostSurvey } from '@/api/survey/postSurvey'; @@ -16,7 +15,6 @@ const SurveyPage: React.FC = () => { localStorage.setItem('surveyVisited', 'true'); }, []); - const navigate = useNavigate(); const handleSurveyButtonClick = () => { @@ -25,7 +23,6 @@ const SurveyPage: React.FC = () => { const handleSurvey = async () => { try { - console.log( '나이는:', age, @@ -39,31 +36,34 @@ const SurveyPage: React.FC = () => { path, '/목적은(복수선택):', purpose, - + ); + const SurveyRequest = await PostSurvey({ + age: numAge, + gender: gender, + occupation: job, + region: city, + source: path, + purposes: purpose, + }); console.log('설문조사 전송 성공', SurveyRequest); alert('설문조사가 전송되었습니다.'); navigate('/'); - } catch (error) { console.error('실패입니다.', error); } }; const [age, setAge] = useState(''); - const [gender, setGender] = useState('FEMALE'); const [job, setJob] = useState(''); const [city, setCity] = useState('서울'); const [path, setPath] = useState(''); const [purpose, setPurpose] = useState(); - const handleAgeChange = (age: string) => { setAge(age); }; - const numAge: number = parseInt(age, 10); - const handleGenderChange = (gender: string) => { setGender(gender); }; @@ -76,12 +76,10 @@ const SurveyPage: React.FC = () => { const handlePathChange = (path: string) => { setPath(path); }; - const handlePurposeChange = (purpose: string[]) => { setPurpose(purpose); }; - return ( <> @@ -99,9 +97,7 @@ const SurveyPage: React.FC = () => { - - From e1b4a8c256405e23958df82b4fe402abf96f88ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=B5=EC=A0=95=EB=AF=BC?= <102538362+jeongmin59@users.noreply.github.com> Date: Sun, 21 Apr 2024 14:33:36 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[FE][Fix]=20=ED=8C=80=EC=9B=90=20=EC=B4=88?= =?UTF-8?q?=EB=8C=80=20=EC=88=98=EB=9D=BD=20api=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :recycle: Refactor: 초대 코드 관련 * :bug: Fix: 쿼리파라미터로 수정 * :lipstick: Design: 초대 버튼 만듦 --- src/App.tsx | 5 +- src/api/@types/InviteTeam.ts | 1 - src/api/inviteTeamApi/postInviteTeam.tsx | 4 +- src/components/inviteTeam/AcceptInvite.tsx | 49 +++++++++++++------ src/components/inviteTeam/InviteTeamModal.tsx | 13 +++-- src/styles/inviteTeam/AcceptInvite.ts | 13 +++++ 6 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 src/styles/inviteTeam/AcceptInvite.ts diff --git a/src/App.tsx b/src/App.tsx index da4f346..d3c73d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,7 @@ import RetroRevisePage from './pages/RevisePage'; import RetroTeamPage from './pages/SectionPage'; import MainLayout from '@/components/layout/MainLayout'; import ProfileLayout from '@/components/layout/ProfileLayout'; +// import AcceptInvitePage from '@/pages/AccpetInvitePage'; import AuthPage from '@/pages/AuthPage'; import CreateRetroPage from '@/pages/CreateRetroPage'; import HomePage from '@/pages/HomePage'; @@ -131,7 +132,9 @@ const App = () => { } /> - + {/* 발급 될 초대 링크 */} + + {/* } /> */} diff --git a/src/api/@types/InviteTeam.ts b/src/api/@types/InviteTeam.ts index 670852e..7102972 100644 --- a/src/api/@types/InviteTeam.ts +++ b/src/api/@types/InviteTeam.ts @@ -20,4 +20,3 @@ export interface InviteTeamData { export interface PostInviteTeamRequest { invitationCode: string; } -export interface PostInviteTeamResponse {} diff --git a/src/api/inviteTeamApi/postInviteTeam.tsx b/src/api/inviteTeamApi/postInviteTeam.tsx index 394bb16..10867c1 100644 --- a/src/api/inviteTeamApi/postInviteTeam.tsx +++ b/src/api/inviteTeamApi/postInviteTeam.tsx @@ -3,8 +3,8 @@ import axiosInstance from '../axiosConfig'; const postInviteTeam = async (invitationId: string) => { try { - const response = await axiosInstance.post('/team/accept-invitation', { - invitationCode: invitationId, + const response = await axiosInstance.post('/teams/accept-invitation', { + invitationCode: invitationId, // useParams 로 받아온 코드 }); console.log('팀원 초대 성공', response.data); return response.data; diff --git a/src/components/inviteTeam/AcceptInvite.tsx b/src/components/inviteTeam/AcceptInvite.tsx index bf8812b..e60f205 100644 --- a/src/components/inviteTeam/AcceptInvite.tsx +++ b/src/components/inviteTeam/AcceptInvite.tsx @@ -1,38 +1,55 @@ import React, { useEffect, useState } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { Button, Text } from '@chakra-ui/react'; import postInviteTeam from '@/api/inviteTeamApi/postInviteTeam'; +import * as S from '@/styles/inviteTeam/AcceptInvite'; const AcceptInvite: React.FC = () => { - const { invitationId } = useParams<{ invitationId?: string }>(); + const location = useLocation(); const navigate = useNavigate(); const [inviteSuccess, setInviteSuccess] = useState(false); - useEffect(() => { - const acceptInvitation = async () => { - try { - if (invitationId) { - await postInviteTeam(invitationId); + const searchParams = new URLSearchParams(location.search); + const invitationCode = searchParams.get('invitationId'); + + const acceptInvitation = async () => { + try { + if (invitationCode) { + const response = await postInviteTeam(invitationCode); + // 백엔드에서 204 반환해줌 + if (response.status === 204) { setInviteSuccess(true); // 초대 요청이 성공했을 때 상태를 true로 변경 } else { - console.error('InvitationId 추출 실패'); + console.error('초대 수락 실패'); } - } catch (error) { - console.error('에러', error); + } else { + console.error('invitationCode 추출 실패'); } - }; - - acceptInvitation(); - }, [invitationId]); + } catch (error) { + console.error('에러', error); + } + }; useEffect(() => { if (inviteSuccess) { // 초대 성공 시 알림을 띄우고 retrolist 페이지로 navigate - alert('초대 성공했습니다!'); + alert('초대를 성공적으로 수락했습니다!'); navigate('/retrolist'); } }, [inviteSuccess, navigate]); - return
초대를 수락하는 중...
; + return ( + <> + + + 초대를 수락하시겠습니까? + + + + + ); }; export default AcceptInvite; diff --git a/src/components/inviteTeam/InviteTeamModal.tsx b/src/components/inviteTeam/InviteTeamModal.tsx index ad48fa7..f440270 100644 --- a/src/components/inviteTeam/InviteTeamModal.tsx +++ b/src/components/inviteTeam/InviteTeamModal.tsx @@ -46,10 +46,17 @@ const InviteTeamModal: React.FC = ({ isOpen, onClose, team fetchInviteData(); }, []); + const generateInvitationUrl = (invitationCode: string) => { + // const domain = 'http://localhost:3000'; // 로컬 테스트용 + const domain = 'https://www.pastforward.link'; // 배포용 + return `${domain}/invitations?invitationId=${invitationCode}`; + }; + const copyToClipboard = () => { if (inviteData) { // inviteData가 null이 아닐 때만 실행 - navigator.clipboard.writeText(inviteData.invitationUrl).then( + const invitationUrl = generateInvitationUrl(inviteData.invitationCode); + navigator.clipboard.writeText(invitationUrl).then( () => { setShowAlert(true); // 알림 표시 }, @@ -71,11 +78,11 @@ const InviteTeamModal: React.FC = ({ isOpen, onClose, team {inviteData && ( - + QR과 Link를 통해 팀원을 초대하여 회고를 함께하세요! - + diff --git a/src/styles/inviteTeam/AcceptInvite.ts b/src/styles/inviteTeam/AcceptInvite.ts new file mode 100644 index 0000000..dac61b5 --- /dev/null +++ b/src/styles/inviteTeam/AcceptInvite.ts @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; +`; + +export const TextContainer = styled.div` + margin-bottom: 2rem; +`;