diff --git a/package.json b/package.json index 91c887b..ea09032 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "react-datepicker": "^6.4.0", "react-dom": "^18.2.0", "react-fullpage": "^0.1.19", - "react-icons": "^5.0.1", + "react-icons": "^5.1.0", "react-router-dom": "^6.22.3", "react-select": "^5.8.0", "recoil": "^0.7.7", diff --git a/src/App.tsx b/src/App.tsx index f6001b7..da4f346 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,8 +3,9 @@ import { Navigate, useLocation } from 'react-router-dom'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { getCurrentUser } from 'aws-amplify/auth'; import { RecoilRoot } from 'recoil'; -import RetroTeamPage from './pages/RetroTeamPage'; +import AcceptInvite from './components/inviteTeam/AcceptInvite'; import RetroRevisePage from './pages/RevisePage'; +import RetroTeamPage from './pages/SectionPage'; import MainLayout from '@/components/layout/MainLayout'; import ProfileLayout from '@/components/layout/ProfileLayout'; import AuthPage from '@/pages/AuthPage'; @@ -12,7 +13,6 @@ import CreateRetroPage from '@/pages/CreateRetroPage'; import HomePage from '@/pages/HomePage'; import MyPage from '@/pages/MyPage'; import RetroListPage from '@/pages/RetroListPage'; -import RetroPersonalPage from '@/pages/RetroPersonalPage'; import SurveyPage from '@/pages/SurveyPage'; interface PrivateRouteProps { @@ -99,14 +99,6 @@ const App = () => { } /> - - - - } - /> { } /> - { } /> - {/* MainLayout */} }> } /> @@ -141,6 +131,7 @@ const App = () => { } /> + diff --git a/src/api/@types/@asConst.ts b/src/api/@types/@asConst.ts index ca209e2..d04de52 100644 --- a/src/api/@types/@asConst.ts +++ b/src/api/@types/@asConst.ts @@ -1,9 +1,10 @@ export const Order = { - RECENTLY: 'RECENTLY', - PREVIOUSLY: 'PREVIOUSLY', + NEWEST: 'NEWEST', + OLDEST: 'OLDEST', } as const; export const Status = { + ALL: 'ALL', NOT_STARTED: 'NOT_STARTED', IN_PROGRESS: 'IN_PROGRESS', COMPLETED: 'COMPLETED', @@ -12,7 +13,7 @@ export const Status = { export const RetrospectiveType = { TEAM: 'TEAM', PERSONAL: 'PERSONAL', -}; +} as const; export const Template = { 1: 'Keep', 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/InviteTeam.ts b/src/api/@types/InviteTeam.ts index 203f777..670852e 100644 --- a/src/api/@types/InviteTeam.ts +++ b/src/api/@types/InviteTeam.ts @@ -1,3 +1,4 @@ +// get export interface GetInviteTeamRequest { // teamId: number; } @@ -14,3 +15,9 @@ export interface InviteTeamData { expirationTime: string; qrCodeImage: string; } + +// post +export interface PostInviteTeamRequest { + invitationCode: string; +} +export interface PostInviteTeamResponse {} diff --git a/src/api/@types/Retrospectives.ts b/src/api/@types/Retrospectives.ts index bab084b..f32c722 100644 --- a/src/api/@types/Retrospectives.ts +++ b/src/api/@types/Retrospectives.ts @@ -1,4 +1,4 @@ -import { TRetrospective, TStatus } from './@asConst'; +import { TRetrospective, TStatus, TOrder } from './@asConst'; //onlyGet export interface onlyGetRetrospectiveRequest { @@ -12,35 +12,51 @@ export interface onlyGetRetrospectiveResponse { } export interface RetrospectiveData { - description: string; retrospectiveId: number; - status: keyof TStatus; - teamId: number; - templateId: number; - thumbnail: string; title: string; + templateId: number; + type: keyof TRetrospective; userId: number; + leaderName: string; + description: string; + status: string; + thumbnail: string; } // get export interface GetRetrospectiveRequest { page: number; size: number; - order: keyof TStatus; + order: keyof TOrder; status: keyof TStatus; keyword: string; isBookmarked: boolean; } +export interface GetRetrospectiveResponseNodes { + // 추가 + id: number; + title: string; + userId: number; + teamId: number | null; + templateId: number; + status: keyof TStatus; + isBookmarked: boolean; + thumbnail: string; + startDate: string; + createdDate: string; + updatedDate: string; + username: string; +} + export interface GetRetrospectiveData { code: number; message: string; data: { totalCount: number; - nodes: Array; + nodes: Array; //RetrospectiveResponse 에서 변경 }; } - //post export interface PostRetrospectivesRequest { title: string; @@ -49,7 +65,7 @@ export interface PostRetrospectivesRequest { templateId: number; status: keyof TStatus; thumbnail: string | null; - startDate: string; + startDate: Date | string; description: string; } @@ -69,12 +85,20 @@ export interface DeleteRetrospectiveRequest { } //put -export interface PutRetrospectiveRequest { +export interface PutTeamRetrospectiveRequest { retrospectiveId: number; title: string; teamId?: number; description: string; - status: keyof TStatus; + status: string; + thumbnail?: string; +} + +export interface PutPersonalRetrospectiveRequest { + retrospectiveId: number; + title: string; + description: string; + status: string; thumbnail?: string; } @@ -85,7 +109,7 @@ export interface RetrospectiveResponse { id: number; title: string; userId: number; - teamId: number; + teamId: number | null; templateId: number; status: keyof TStatus; isBookmarked: boolean; @@ -96,10 +120,23 @@ export interface RetrospectiveResponse { }; } +export interface PatchRetrospectiveRequest { + retrospectiveId: number; + // userId: number; +} + +export interface PatchRetrospectiveResponse { + code: number; + message: string; + data: boolean; +} + export interface RetrospectivesClient { onlyGet(request: onlyGetRetrospectiveRequest): Promise; 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/@types/Section.ts b/src/api/@types/Section.ts index 16e87cc..1846f7e 100644 --- a/src/api/@types/Section.ts +++ b/src/api/@types/Section.ts @@ -1,7 +1,11 @@ //get -export interface GetSectionRequest { +export interface TeamGetSectionRequest { + retrospectiveId: number; + teamId: number; +} + +export interface PersonalGetSectionRequest { retrospectiveId: number; - teamId: number | null; } export interface sectionData { @@ -35,12 +39,14 @@ export interface CreateSectionRequest { export interface PostSectionResponse { code: number; message: string; - data: { - id: number; - userId: number; - retrospectiveId: number; - sectionContent: string; - }; + data: PostSectionData; +} + +export interface PostSectionData { + id: number; + userId: number; + retrospectiveId: number; + sectionContent: string; } //patch @@ -83,7 +89,8 @@ export interface PostLikeSectionResponse { }; } export interface SectionClient { - get(request: GetSectionRequest): Promise; + TeamGet(request: TeamGetSectionRequest): Promise; + PersonalGet(request: PersonalGetSectionRequest): Promise; create(request: CreateSectionRequest): Promise; patch(request: PatchSectionRequest): Promise; delete(request: DeleteSectionRequest): Promise; diff --git a/src/api/@types/Survey.ts b/src/api/@types/Survey.ts index 3d1b2aa..ee816f5 100644 --- a/src/api/@types/Survey.ts +++ b/src/api/@types/Survey.ts @@ -6,6 +6,7 @@ export interface PostSurveyRequest { region: string; source: string; purposes: string[] | undefined; + } export interface PostSurveyResponse { diff --git a/src/api/@types/TeamController.tsx b/src/api/@types/TeamController.tsx index d3c53ed..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; @@ -13,8 +14,42 @@ export interface TeamMembersData { userId: number; username: string; profileImage: string; + email: string; + joinedAt: string; } +//getTemplateName +export interface GetTemplateNameRequest { + templateId: number; +} + +export interface GetTemplateNameResponse { + code: number; + message: string; + data: TemplateNameData[]; +} + +export interface TemplateNameData { + id: number; + name: string; + templateId: number; + sequence: number; +} + +// put 담당자 +export interface PutActionItemsRequest { + teamId: number; + retrospectiveId: number; + sectionId: number; +} + +export interface PutActionItemsResponse { + code: number; + message: string; + data: object; +} export interface TeamControllerClient { - get(request: GetTeamMembersRequest): Promise; + TeamMemberGet(request: GetTeamMembersRequest): Promise; + TemplateNameGet(request: GetTemplateNameRequest): Promise; + ActionItemsMemberPut(request: PutActionItemsRequest): Promise; } diff --git a/src/api/@types/Thumbnail.ts b/src/api/@types/Thumbnail.ts index 7f72931..950ae0a 100644 --- a/src/api/@types/Thumbnail.ts +++ b/src/api/@types/Thumbnail.ts @@ -5,8 +5,12 @@ export interface PostImageToS3Request { } export interface PostImageToS3Response { - filename: string; - preSignedUrl: string; + code: number; + data: { + filename: string; + preSignedUrl: string; + }; + message: string | null; } // 유저 프로필 사진 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/__mock__/retrospective.ts b/src/api/__mock__/retrospective.ts index 5e977a8..939dea6 100644 --- a/src/api/__mock__/retrospective.ts +++ b/src/api/__mock__/retrospective.ts @@ -18,7 +18,6 @@ export const MockRetrospective: RetrospectiveResponse = { updatedDate: '2024-04-12T04:20:54.835Z', }, }; - export const MockOnlyGetRetrospective: onlyGetRetrospectiveResponse = { code: 202, message: 'string', @@ -26,10 +25,11 @@ export const MockOnlyGetRetrospective: onlyGetRetrospectiveResponse = { retrospectiveId: 1, title: 'heeeeee', templateId: 2, - teamId: 1, - userId: 1, - description: 'heeee', - status: Status.COMPLETED, + type: 'TEAM', + userId: 0, + leaderName: 'string', + description: 'string', + status: Status.NOT_STARTED, thumbnail: '3fa85f64-5717-4562-b3fc-2c963f66afa6', }, }; diff --git a/src/api/__mock__/retrospectiveTemplate.ts b/src/api/__mock__/retrospectiveTemplate.ts deleted file mode 100644 index aa13607..0000000 --- a/src/api/__mock__/retrospectiveTemplate.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RetrospectivesTemplateResponse } from '../@types/RetrospectiveTemplates'; - -export const mockRetrospectiveTemplate: RetrospectivesTemplateResponse[] = [ - // { id: 1, name: 'hee' }, - // { - // id: 2, - // name: 'jung', - // }, -]; diff --git a/src/api/__mock__/teamMembers.ts b/src/api/__mock__/teamMembers.ts index 92b32ad..148ba85 100644 --- a/src/api/__mock__/teamMembers.ts +++ b/src/api/__mock__/teamMembers.ts @@ -1,15 +1,10 @@ -import { GetTeamMembersResponse, TeamMembersData } from '../@types/TeamController'; +import { GetTeamMembersResponse } from '../@types/TeamController'; export const MockTeamMembers: GetTeamMembersResponse = { code: 1, message: 'no', data: [ - { userId: 1, username: 'gg', profileImage: 'gggg' }, - { userId: 1, username: 'ff', profileImage: 'gggg' }, + { userId: 1, username: 'gg', profileImage: 'gggg', email: 'test@gmail.com', joinedAt: '2024-04-18T13:59:52.739Z' }, + { userId: 1, username: 'ff', profileImage: 'gggg', email: 'test@gmail.com', joinedAt: '2024-04-18T13:59:52.739Z' }, ], }; - -export const MockTeamMembersData: TeamMembersData[] = [ - { userId: 1, username: 'gg', profileImage: 'gggg' }, - { userId: 1, username: 'ff', profileImage: 'gggg' }, -]; diff --git a/src/api/client.ts b/src/api/client.ts index 1e0575d..00b30c9 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -6,7 +6,6 @@ import { unwrapResponse } from './interceptors/response'; // import { logAndProcessError, logResponse, unwrapResponse } from './interceptors/response'; // import { flow } from '@/utils/flow'; - export const mswInstance = axios.create({ baseURL: '/', timeout: 4000, @@ -20,10 +19,7 @@ export const mswInstance = axios.create({ // withCredentials: true, // }); - - axiosInstance.interceptors.request.use(logRequest); // axiosInstance.interceptors.response.use(flow([logResponse, unwrapResponse]), logAndProcessError); - mswInstance.interceptors.response.use(unwrapResponse); diff --git a/src/api/imageApi/postImageToS3.tsx b/src/api/imageApi/postImageToS3.tsx index f8d5da1..10cb724 100644 --- a/src/api/imageApi/postImageToS3.tsx +++ b/src/api/imageApi/postImageToS3.tsx @@ -5,10 +5,10 @@ import { PostImageToS3Request, PostImageToS3Response } from '@/api/@types/Thumbn const postImageToS3 = async (requestData: PostImageToS3Request): Promise => { try { const response = await axiosInstance.post('/s3/pre-signed-url', requestData); - console.log('사진 s3 업로드 성공', response.data); + console.log('사진 s3 요청 성공', response.data); return response.data; } catch (error) { - throw new Error('s3 업로드 실패'); + throw new Error('s3 요청 실패'); } }; diff --git a/src/api/retrospectivesApi/getInviteTeam.tsx b/src/api/inviteTeamApi/getInviteTeam.tsx similarity index 76% rename from src/api/retrospectivesApi/getInviteTeam.tsx rename to src/api/inviteTeamApi/getInviteTeam.tsx index b041b9e..215651a 100644 --- a/src/api/retrospectivesApi/getInviteTeam.tsx +++ b/src/api/inviteTeamApi/getInviteTeam.tsx @@ -4,10 +4,10 @@ import axiosInstance from '../axiosConfig'; const getInviteTeam = async (teamId: GetInviteTeamRequest): Promise => { try { const response = await axiosInstance.get(`/teams/${teamId}/invitation-url`); - console.log('팀원 초대 호출 성공', response.data); + console.log('팀원 초대 링크 호출 성공', response.data); return response.data; } catch (error) { - throw new Error('팀원 초대 호출 실패'); + throw new Error('팀원 초대 링크 호출 실패'); } }; diff --git a/src/api/inviteTeamApi/postInviteTeam.tsx b/src/api/inviteTeamApi/postInviteTeam.tsx new file mode 100644 index 0000000..394bb16 --- /dev/null +++ b/src/api/inviteTeamApi/postInviteTeam.tsx @@ -0,0 +1,16 @@ +// import { PostInviteTeamRequest } from '../@types/InviteTeam'; +import axiosInstance from '../axiosConfig'; + +const postInviteTeam = async (invitationId: string) => { + try { + const response = await axiosInstance.post('/team/accept-invitation', { + invitationCode: invitationId, + }); + console.log('팀원 초대 성공', response.data); + return response.data; + } catch (error) { + throw new Error('팀원 초대 실패'); + } +}; + +export default postInviteTeam; diff --git a/src/api/retrospectivesApi/getRetrospective.tsx b/src/api/retrospectivesApi/getRetrospective.tsx new file mode 100644 index 0000000..c0d027d --- /dev/null +++ b/src/api/retrospectivesApi/getRetrospective.tsx @@ -0,0 +1,30 @@ +import { GetRetrospectiveData, GetRetrospectiveRequest } from '@/api/@types/Retrospectives'; +import axiosInstance from '@/api/axiosConfig'; + +export const queryGetRetrospective = async (requestData: GetRetrospectiveRequest): Promise => { + try { + const { page, size, order, status, keyword, isBookmarked } = requestData; + const params: { [key: string]: any } = { + page, + size, + order, + isBookmarked, + }; + if (status !== 'ALL') { + params.status = status; + } + if (keyword !== '') { + params.keyword = keyword; + } + + console.log(params); + const response = await axiosInstance.get('/retrospectives', { + params, + }); + // console.log('회고 get 성공', response.data); + return response.data; + } catch (error) { + // console.log('회고 get 실패', error); + throw new Error('회고 get 실패'); + } +}; diff --git a/src/api/retrospectivesApi/patchRetrospective.tsx b/src/api/retrospectivesApi/patchRetrospective.tsx new file mode 100644 index 0000000..ae3c4e5 --- /dev/null +++ b/src/api/retrospectivesApi/patchRetrospective.tsx @@ -0,0 +1,17 @@ +import { PatchRetrospectiveRequest, PatchRetrospectiveResponse } from '../@types/Retrospectives'; +import axiosInstance from '@/api/axiosConfig'; + +export const patchRetrospective = async ( + requestData: PatchRetrospectiveRequest, +): Promise => { + try { + console.log(requestData.retrospectiveId, 'patch 요청'); + const response = await axiosInstance.patch( + `/retrospectives/${requestData.retrospectiveId}/bookmark`, + ); + console.log('북마크 patch 성공', response.data); + return response.data; + } catch (error) { + throw new Error('북마크 patch 실패'); + } +}; 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 7b56789..0bd58a2 100644 --- a/src/api/services/Retrospectives.ts +++ b/src/api/services/Retrospectives.ts @@ -9,7 +9,6 @@ import { RetrospectivesClient, } from '../@types/Retrospectives'; import axiosInstance from '../axiosConfig'; -import { mswInstance } from '../client'; const ROUTE = 'retrospectives'; @@ -19,7 +18,7 @@ export const RetrospectiveService: RetrospectivesClient = { const response = await axiosInstance.get(`${ROUTE}/${retrospectiveId}`); return response.data; } catch (error) { - throw new Error('템플릿 조회 실패'); + throw new Error(error as string); } }, create: async (request: PostRetrospectivesRequest): Promise => { @@ -32,7 +31,7 @@ export const RetrospectiveService: RetrospectivesClient = { }, get: async (request: GetRetrospectiveRequest): Promise => { try { - const response = await mswInstance.get(`${ROUTE}/`, { + const response = await axiosInstance.get(`${ROUTE}/`, { params: request, }); return response.data; @@ -43,13 +42,23 @@ export const RetrospectiveService: RetrospectivesClient = { delete: async ({ retrospectiveId }: DeleteRetrospectiveRequest): Promise => { try { - const response = await mswInstance.delete(`${ROUTE}/${retrospectiveId}`); + const response = await axiosInstance.delete(`${ROUTE}/${retrospectiveId}`); + return response.data; + } catch (error) { + throw new Error(error as string); + } + }, + + putTeam: async ({ retrospectiveId, ...request }) => { + try { + const response = await axiosInstance.put(`${ROUTE}/${retrospectiveId}`, request); return response.data; } catch (error) { throw new Error(error as string); } }, - put: async ({ retrospectiveId }, ...request) => { + + putPersonal: async ({ retrospectiveId, ...request }) => { try { const response = await axiosInstance.put(`${ROUTE}/${retrospectiveId}`, request); return response.data; @@ -57,4 +66,8 @@ export const RetrospectiveService: RetrospectivesClient = { throw new Error(error as string); } }, + + patch: async (retrospectiveId, ...request) => { + return await axiosInstance.patch(`${ROUTE}/${retrospectiveId}/bookmark`, request); + }, }; diff --git a/src/api/services/Section.ts b/src/api/services/Section.ts index 9f95b1c..4bef268 100644 --- a/src/api/services/Section.ts +++ b/src/api/services/Section.ts @@ -2,19 +2,28 @@ import { CreateSectionRequest, DeleteSectionRequest, DeleteSectionResponse, - GetSectionRequest, + TeamGetSectionRequest, GetSectionResponse, PostLikeSectionResponse, PostLikesSectionRequest, PostSectionResponse, SectionClient, + PersonalGetSectionRequest, } from '../@types/Section'; import axiosInstance from '../axiosConfig'; const ROUTE = 'sections'; export const SectionServices: SectionClient = { - get: async (request: GetSectionRequest): Promise => { + TeamGet: async (request: TeamGetSectionRequest): Promise => { + try { + const response = await axiosInstance.get(`${ROUTE}`, { params: request }); + return response.data; + } catch (error) { + throw new Error(error as string); + } + }, + PersonalGet: async (request: PersonalGetSectionRequest): Promise => { try { const response = await axiosInstance.get(`${ROUTE}`, { params: request }); return response.data; @@ -24,7 +33,7 @@ export const SectionServices: SectionClient = { }, create: async (request: CreateSectionRequest): Promise => { try { - const response = await axiosInstance.post(`${ROUTE}/`, request); + const response = await axiosInstance.post(`${ROUTE}`, request); return response.data; } catch (error) { throw new Error(error as string); diff --git a/src/api/services/TeamController.ts b/src/api/services/TeamController.ts index c93fcb3..e40f33c 100644 --- a/src/api/services/TeamController.ts +++ b/src/api/services/TeamController.ts @@ -1,12 +1,36 @@ -import { GetTeamMembersRequest, TeamControllerClient } from '../@types/TeamController'; -import axiosInstance from '../axiosConfig'; +import { + GetTeamMembersRequest, + GetTemplateNameRequest, + GetTemplateNameResponse, + TeamControllerClient, + PutActionItemsRequest, + PutActionItemsResponse, +} from '@/api/@types/TeamController'; +import axiosInstance from '@/api/axiosConfig'; -const ROUTE = 'teams'; +const TEAMS_ROUTE = 'teams'; +const TEMPLATE_ROUTE = 'retrospective-templates'; export const TeamControllerServices: TeamControllerClient = { - get: async ({ teamId, ...request }: GetTeamMembersRequest) => { + TeamMemberGet: async ({ teamId, ...request }: GetTeamMembersRequest) => { try { - const response = await axiosInstance.get(`${ROUTE}/${teamId}/users`, { params: request }); + const response = await axiosInstance.get(`${TEAMS_ROUTE}/${teamId}/users`, { params: request }); + return response.data; + } catch (error) { + throw new Error(error as string); + } + }, + TemplateNameGet: async ({ templateId }: GetTemplateNameRequest): Promise => { + try { + const response = await axiosInstance.get(`${TEMPLATE_ROUTE}/${templateId}/template-sections`); + return response.data; + } catch (error) { + 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/assets/BookmarkIcon_N.png b/src/assets/BookmarkIcon_N.png deleted file mode 100644 index 176bef5..0000000 Binary files a/src/assets/BookmarkIcon_N.png and /dev/null differ diff --git a/src/assets/BookmarkIcon_Y.png b/src/assets/BookmarkIcon_Y.png deleted file mode 100644 index 786cd9f..0000000 Binary files a/src/assets/BookmarkIcon_Y.png and /dev/null differ diff --git a/src/assets/Link.png b/src/assets/Link.png deleted file mode 100644 index 45aa0fb..0000000 Binary files a/src/assets/Link.png and /dev/null differ diff --git a/src/assets/ModalClose.png b/src/assets/ModalClose.png deleted file mode 100644 index c5a7c19..0000000 Binary files a/src/assets/ModalClose.png and /dev/null differ diff --git a/src/assets/TeamIcon.png b/src/assets/TeamIcon.png deleted file mode 100644 index 6a00cfe..0000000 Binary files a/src/assets/TeamIcon.png and /dev/null differ diff --git a/src/assets/UserProfile1.png b/src/assets/UserProfile1.png new file mode 100644 index 0000000..f411a96 Binary files /dev/null and b/src/assets/UserProfile1.png differ diff --git a/src/components/RetroList/BookmarkButton.tsx b/src/components/RetroList/BookmarkButton.tsx index ed217cb..a1bca6f 100644 --- a/src/components/RetroList/BookmarkButton.tsx +++ b/src/components/RetroList/BookmarkButton.tsx @@ -1,18 +1,22 @@ import { useState } from 'react'; -import BookmarkIcon from '@/assets/BookmarkIcon_Y.png'; import * as S from '@/styles/RetroList/BookmarkButton.styles'; -const BookmarkButton: React.FC = () => { +interface BookmarkButtonProps { + handleBookmarkButton: (option: boolean) => void; +} + +const BookmarkButton: React.FC = ({ handleBookmarkButton }) => { const [isBookmarked, setIsBookmarked] = useState(false); const handleBookmark = () => { setIsBookmarked(!isBookmarked); + handleBookmarkButton(!isBookmarked); }; return ( <> - + Bookmark diff --git a/src/components/RetroList/ContentsList.tsx b/src/components/RetroList/ContentsList.tsx index ef28637..b52b03c 100644 --- a/src/components/RetroList/ContentsList.tsx +++ b/src/components/RetroList/ContentsList.tsx @@ -1,14 +1,16 @@ -import { useState } from 'react'; -import BookmarkIcon_N from '@/assets/BookmarkIcon_N.png'; -import BookmarkIcon_Y from '@/assets/BookmarkIcon_Y.png'; -import MoreIcon from '@/assets/MoreIcon.png'; -import PersonalIcon from '@/assets/PersonalIcon.png'; -import ProgressBefore from '@/assets/Progress_Before.png'; -import ProgressDone from '@/assets/Progress_Done.png'; -import ProgressIng from '@/assets/Progress_Ing.png'; -import TeamIcon from '@/assets/TeamIcon.png'; +import { useEffect, useState } from 'react'; +import { CgTimelapse } from 'react-icons/cg'; // ing +import { FaRegCircleCheck } from 'react-icons/fa6'; // done +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 { 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 { useCustomToast } from '@/hooks/useCustomToast'; import * as S from '@/styles/RetroList/ContentsList.styles'; interface Content { @@ -23,16 +25,35 @@ interface Content { startDate: string; createdDate: string; updatedDate: string; + username: string; } interface ContentListProps { data: Content[]; viewMode: string; searchData: string; + setBookmarkUpdate: React.Dispatch>; } -const ContentList: React.FC = ({ data, viewMode, searchData }) => { +const ContentList: React.FC = ({ data, viewMode, searchData, setBookmarkUpdate }) => { + // const [contentData, setContentData] = useState(data); 받아온데이터 const [openModalId, setOpenModalId] = useState(null); + const toast = useCustomToast(); + const [image, setImage] = useState<{ [key: number]: string }>({}); + + const handleBookmark = async (itemId: number) => { + try { + const requestData: PatchRetrospectiveRequest = { + retrospectiveId: itemId, + }; + const response = await patchRetrospective(requestData); + console.log('북마크 patch 요청 완료', response); + setBookmarkUpdate(prev => !prev); + } catch (error) { + // console.error('북마크 patch 요청 실패:', error); + toast.error(error); + } + }; const openModalForItem = (itemId: number) => { setOpenModalId(itemId); @@ -43,6 +64,43 @@ const ContentList: React.FC = ({ data, viewMode, searchData }) }; const filteredData = data.filter(item => item.title.toLowerCase().includes(searchData.toLowerCase())); + 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); + const options: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }; + return localTime.toLocaleString(undefined, options); // 로컬 타임존으로 변환하여 문자열로 반환 + }; + + useEffect(() => { + const fetchThumbnailsData = async (item: Content) => { + try { + if (item.thumbnail) { + const imageResponse = await postImageToS3({ + filename: item.thumbnail, + method: 'GET', + }); + console.log('s3 사진 받아오기 성공', imageResponse.data.preSignedUrl); + setImage(prevImage => ({ + ...prevImage, + [item.id]: imageResponse.data.preSignedUrl, + })); + } + } catch (error) { + console.error(error); + } + }; + + data.forEach(item => fetchThumbnailsData(item)); + }, [data]); return (
@@ -51,31 +109,62 @@ const ContentList: React.FC = ({ data, viewMode, searchData }) {filteredData.map(item => ( - +
- - {item.title} + {item.teamId && } {!item.teamId && } + navigate(`/sections?retrospectiveId=${item.id}&teamId=${item.teamId}`)}> + {item.title} +
-
- {/* 북마크 patch */} - openModalForItem(item.id)} /> +
+ {item.isBookmarked && ( + handleBookmark(item.id)} style={{ color: '#fcea12' }} size="19" /> + )} + {!item.isBookmarked && handleBookmark(item.id)} size={20} />} + openModalForItem(item.id)} + onClick={() => { + if (item.userId === item.id) { + // 수정 권한 없을 때(생성자가 아닐 때 확인하고 고치기) + openModalForItem(item.id); + } else { + navigate(`/revise?retrospectiveId=${item.id}&teamId=${item.teamId}`); + } + }} + />
- {item.userId} + {item.username}
- {item.createdDate} 수정 - {/* 수정하면 수정 시각으로 변경*/} - + + {item.updatedDate !== item.createdDate + ? `${convertToLocalTime(item.updatedDate)} 수정` + : convertToLocalTime(item.createdDate)} + {/* {item.updatedDate !== item.createdDate ? `${item.updatedDate} 수정` : item.createdDate} */} + + {item.status === 'NOT_STARTED' && ( + + )} + {item.status === 'IN_PROGRESS' && ( + + )} + {item.status === 'COMPLETED' && ( + + )} @@ -96,29 +185,50 @@ const ContentList: React.FC = ({ data, viewMode, searchData })
{filteredData.map(item => ( - {item.title} - - {/* 생성자 이름 */} - + navigate(`/section?retrospectiveId=${item.id}&teamId=${item.teamId}`)}> + {item.title} + + {item.username} - {item.createdDate} {/* 수정하면 수정 시각으로 변경*/} + {item.updatedDate && item.updatedDate !== item.startDate ? `${item.updatedDate}` : item.startDate} - + {item.isBookmarked && ( + handleBookmark(item.id)} style={{ color: '#fcea12' }} size="19" /> + )} + {!item.isBookmarked && handleBookmark(item.id)} size={20} />} - + {item.status === 'NOT_STARTED' && ( + + )} + {item.status === 'IN_PROGRESS' && ( + + )} + {item.status === 'COMPLETED' && ( + + )} - openModalForItem(item.id)} /> + openModalForItem(item.id)} + onClick={() => { + if (item.userId === item.id) { + // 수정 권한 없을 때(생성자가 아닐 때 확인하고 고치기) + openModalForItem(item.id); + } else { + navigate(`/revise?retrospectiveId=${item.id}&teamId=${item.teamId}`); + } + }} + /> diff --git a/src/components/RetroList/ControlBar.tsx b/src/components/RetroList/ControlBar.tsx deleted file mode 100644 index e2a3ea0..0000000 --- a/src/components/RetroList/ControlBar.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import BookmarkButton from '@/components/RetroList/BookmarkButton'; -import OrderButton from '@/components/RetroList/OrderButton'; -import ProgressButton from '@/components/RetroList/ProgressButton'; - -import * as S from '@/styles/RetroList/ControlBar.styles'; - -const ControlBar: React.FC = () => { - return ( - - - - - - ); -}; - -export default ControlBar; diff --git a/src/components/RetroList/Modal.tsx b/src/components/RetroList/Modal.tsx index d6f5878..ad5757b 100644 --- a/src/components/RetroList/Modal.tsx +++ b/src/components/RetroList/Modal.tsx @@ -1,4 +1,4 @@ -import ModalClose from '@/assets/modalClose.png'; +import { IoIosClose } from 'react-icons/io'; import * as S from '@/styles/RetroList/Modal.styles'; interface ModalProps { @@ -12,7 +12,7 @@ const Modal: React.FC = ({ onClose, isOpen }) => {
- + 생성자가 아닌 참여자는 수정 권한이 없습니다.
diff --git a/src/components/RetroList/OrderButton.tsx b/src/components/RetroList/OrderButton.tsx index 8d454be..18875b7 100644 --- a/src/components/RetroList/OrderButton.tsx +++ b/src/components/RetroList/OrderButton.tsx @@ -5,7 +5,11 @@ import NewestIcon from '@/assets/Newest.png'; import OldestIcon from '@/assets/Oldest.png'; import * as S from '@/styles/RetroList/OrderButton.styles'; -const OrderButton: React.FC = () => { +interface OrderButtonProps { + handleOrder: (option: 'NEWEST' | 'OLDEST') => void; +} + +const OrderButton: React.FC = ({ handleOrder }) => { const order = ['Newest', 'Oldest']; const [isOpen, setIsOpen] = useState(false); const [selectedOrder, setSelectedOrder] = useState(order[0]); @@ -20,7 +24,12 @@ const OrderButton: React.FC = () => { }; const handleOptionClick = (option: string) => { + const order: { [key: string]: string } = { + Newest: 'NEWEST', + Oldest: 'OLDEST', + }; setSelectedOrder(option); + handleOrder(order[option] as 'NEWEST' | 'OLDEST'); setIsOpen(false); }; diff --git a/src/components/RetroList/Pagination.tsx b/src/components/RetroList/Pagination.tsx new file mode 100644 index 0000000..3b79c03 --- /dev/null +++ b/src/components/RetroList/Pagination.tsx @@ -0,0 +1,61 @@ +import React, { useState, useEffect } from 'react'; +import * as S from '@/styles/RetroList/Pagination.styles'; + +interface PaginationProps { + totalPage: number; + limit: number; + page: number; + setPage: React.Dispatch>; +} + +const Pagination: React.FC = ({ totalPage, limit, page, setPage }) => { + const [currentPageArray, setCurrentPageArray] = useState([]); + const [totalPageArray, setTotalPageArray] = useState([]); + + const sliceArrayByLimit = (totalPage: number, limit: number): number[][] => { + const totalPageArray = Array.from(Array(totalPage).keys()); + return Array(Math.ceil(totalPage / limit)) + .fill(null) + .map(() => totalPageArray.splice(0, limit)); + }; + + useEffect(() => { + if (page % limit === 1) { + setCurrentPageArray(totalPageArray[Math.floor(page / limit)]); + } else if (page % limit === 0) { + setCurrentPageArray(totalPageArray[Math.floor(page / limit) - 1]); + } + }, [page, limit, totalPageArray]); + + useEffect(() => { + const slicedPageArray = sliceArrayByLimit(totalPage, limit); + setTotalPageArray(slicedPageArray); + setCurrentPageArray(slicedPageArray[0]); + }, [totalPage, limit]); + + return ( + + + +
+ {currentPageArray?.map(i => ( + setPage(i + 1)} + aria-current={page === i + 1 ? true : undefined} + > + {i + 1} + + ))} +
+ +
+
+ ); +}; + +export default Pagination; diff --git a/src/components/RetroList/ProgressButton.tsx b/src/components/RetroList/ProgressButton.tsx index cd35947..07317d1 100644 --- a/src/components/RetroList/ProgressButton.tsx +++ b/src/components/RetroList/ProgressButton.tsx @@ -7,7 +7,11 @@ import ProgressDone from '@/assets/Progress_Done.png'; import ProgressIng from '@/assets/Progress_Ing.png'; import * as S from '@/styles/RetroList/ProgressButton.style'; -const ProgressButton: React.FC = () => { +interface StatusProps { + handleStatus: (option: 'ALL' | 'NOT_STARTED' | 'IN_PROGRESS' | 'COMPLETED') => void; +} + +const ProgressButton: React.FC = ({ handleStatus }) => { const options = ['ALL', 'Before', 'Ing', 'Done']; const [isOpen, setIsOpen] = useState(false); const [selectedOption, setSelectedOption] = useState(options[0]); @@ -24,7 +28,14 @@ const ProgressButton: React.FC = () => { }; const handleOptionClick = (option: string) => { + const status: { [key: string]: string } = { + ALL: 'ALL', + Before: 'NOT_STARTED', + Ing: 'IN_PROGRESS', + Done: 'COMPLETED', + }; setSelectedOption(option); + handleStatus(status[option] as 'ALL' | 'NOT_STARTED' | 'IN_PROGRESS' | 'COMPLETED'); setIsOpen(false); }; diff --git a/src/components/createRetro/CreateButtonBox.tsx b/src/components/createRetro/CreateButtonBox.tsx index 466ade1..7bb88a7 100644 --- a/src/components/createRetro/CreateButtonBox.tsx +++ b/src/components/createRetro/CreateButtonBox.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { useDisclosure } from '@chakra-ui/react'; +import { TStatus } from '@/api/@types/@asConst'; import PersonalRetroCreateButton from '@/components/createRetro/PersonalRetroCreateButton'; import TeamRetroCreateButton from '@/components/createRetro/TeamRetroCreateButton'; import CreateModal from '@/components/createRetro/modal/CreateModal'; @@ -9,16 +10,19 @@ const CreateButtonBox: React.FC = () => { const { isOpen, onOpen, onClose } = useDisclosure(); const [templateId, setTemplateId] = useState(null); const [type, setType] = useState<'TEAM' | 'PERSONAL'>('TEAM'); + const [status, setStatus] = useState('NOT_STARTED'); const handleTeamButtonClick = () => { setTemplateId(1); setType('TEAM'); // TEAM으로 type 설정 + setStatus('NOT_STARTED'); // 초기 상태 onOpen(); }; const handlePersonalButtonClick = () => { setTemplateId(2); setType('PERSONAL'); // PERSONAL로 type 설정 + setStatus('NOT_STARTED'); // 초기 상태 onOpen(); }; @@ -32,7 +36,7 @@ const CreateButtonBox: React.FC = () => { - + ); }; diff --git a/src/components/createRetro/modal/CreateModal.tsx b/src/components/createRetro/modal/CreateModal.tsx index 5db3192..d097391 100644 --- a/src/components/createRetro/modal/CreateModal.tsx +++ b/src/components/createRetro/modal/CreateModal.tsx @@ -9,7 +9,8 @@ import { ModalCloseButton, Button, } from '@chakra-ui/react'; -import { Status, TRetrospective } from '@/api/@types/@asConst'; +import axios from 'axios'; +import { TRetrospective, TStatus } from '@/api/@types/@asConst'; import { PostRetrospectivesRequest } from '@/api/@types/Retrospectives'; import postImageToS3 from '@/api/imageApi/postImageToS3'; import postRetrospective from '@/api/retrospectivesApi/postRetrospective'; @@ -25,9 +26,10 @@ interface CreateModalProps { onClose: () => void; templateId: number | null; type: keyof TRetrospective; + status: keyof TStatus; } -const CreateModal: React.FC = ({ isOpen, onClose, templateId, type }) => { +const CreateModal: React.FC = ({ isOpen, onClose, templateId, type, status }) => { const size = 'xl'; const navigate = useNavigate(); const [requestData, setRequestData] = useState({ @@ -35,26 +37,42 @@ const CreateModal: React.FC = ({ isOpen, onClose, templateId, type: type, userId: 1, templateId: templateId || 1, - status: Status.NOT_STARTED, + status: status, thumbnail: null, - startDate: '', + startDate: new Date(), description: '', }); + const [image, setImage] = useState(null); useEffect(() => { setRequestData(prevData => ({ ...prevData, templateId: templateId || 1, // templateId가 변경될 때마다 업데이트 type: type, + status: status, })); - }, [templateId, type]); + }, [templateId, type, status]); const handleCreateClick = async () => { try { + let calculatedStatus: keyof TStatus = 'NOT_STARTED'; // 기본값은 'NOT_STARTED'로 설정 + + // startDate 값이 오늘 이전이면 'IN_PROGRESS'로 설정 + const today = new Date(); + const startedDate = new Date(requestData.startDate); + // console.log(startedDate); + if (startedDate <= today) { + calculatedStatus = 'IN_PROGRESS'; + } + + const isoDateString = startedDate.toISOString(); + // console.log(isoDateString); + // 회고 생성 요청 전송 const retrospectiveResponse = await postRetrospective({ ...requestData, - status: Status.NOT_STARTED, + startDate: isoDateString, + status: calculatedStatus, // 계산된 status 값으로 설정 thumbnail: requestData.thumbnail || null, // thumbnail이 없으면 null을 전송 }); @@ -64,7 +82,21 @@ const CreateModal: React.FC = ({ isOpen, onClose, templateId, filename: requestData.thumbnail, // imageUUID를 filename으로 설정 method: 'PUT', }); - console.log('사진 S3 업로드 성공', imageResponse); + console.log('사진 S3 업로드 성공 및 url 확인', imageResponse.data.preSignedUrl); + + const imageUrl = imageResponse.data.preSignedUrl; + + const uploadResponse = await axios.put(imageUrl, image, { + headers: { + 'Content-Type': image?.type, + }, + }); + + if (uploadResponse.status === 200) { + console.log('사진 form-data 성공', uploadResponse); + } else { + console.error('사진 업로드 실패'); + } } console.log('회고 생성 성공', retrospectiveResponse); @@ -79,6 +111,11 @@ const CreateModal: React.FC = ({ isOpen, onClose, templateId, setRequestData({ ...requestData, templateId: selectedTemplateId }); }; + const handleStartDateChange = (startDate: Date) => { + console.log('startDate:', startDate); // startDate를 콘솔에 출력 + setRequestData({ ...requestData, startDate }); // startDate 상태 업데이트 + }; + return ( @@ -89,14 +126,16 @@ const CreateModal: React.FC = ({ isOpen, onClose, templateId, setRequestData({ ...requestData, thumbnail: imageUUID })} + onChange={(file, imageUUID) => { + setRequestData({ ...requestData, thumbnail: imageUUID }); + setImage(file); + }} /> setRequestData({ ...requestData, title })} /> - - setRequestData({ ...requestData, startDate })} /> + diff --git a/src/components/createRetro/modal/ImageUpload.tsx b/src/components/createRetro/modal/ImageUpload.tsx index a44e08c..f8eafa9 100644 --- a/src/components/createRetro/modal/ImageUpload.tsx +++ b/src/components/createRetro/modal/ImageUpload.tsx @@ -3,24 +3,24 @@ import { Input, Box, Image, Button, Center } from '@chakra-ui/react'; import { v4 as uuidv4 } from 'uuid'; interface ImageUploadProps { - onChange: (image: string, uuid: string) => void; // 이미지 파일의 URL, uuid를 외부로 전달하는 함수 + onChange: (file: File | null, uuid: string) => void; // 파일 객체, uuid 함께 전달 } const ImageUpload: React.FC = ({ onChange }) => { const [imagePreview, setImagePreview] = useState(null); - const [, setImageUUID] = useState(null); + const [_, setImageUUID] = useState(null); // 상태를 활용할 수 있도록 수정 const handleImageChange = (event: ChangeEvent) => { const file = event.target.files?.[0]; if (file) { const reader = new FileReader(); + const uuid = uuidv4(); + onChange(file, uuid); // 파일 객체, 이미지 미리보기 URL, UUID를 전달 + reader.onloadend = () => { const result = reader.result as string; - const uuid = uuidv4(); // UUID 생성 - setImageUUID(uuid); // 생성된 UUID 설정 - setImagePreview(result); // 이미지 미리보기 업데이트 - onChange(result, uuid); - console.log(uuid); + setImagePreview(result); + setImageUUID(uuid); }; reader.readAsDataURL(file); } @@ -30,9 +30,7 @@ const ImageUpload: React.FC = ({ onChange }) => { const handleRemoveImage = () => { setImagePreview(null); setImageUUID(null); - if (onChange) { - onChange('', ''); - } + onChange(null, ''); // 이미지 제거 시 null 및 빈 문자열 전달 }; return ( @@ -47,7 +45,6 @@ const ImageUpload: React.FC = ({ onChange }) => { - {/* 이미지 미리보기 */}
{imagePreview && ( diff --git a/src/components/createRetro/modal/StartDateCalender.tsx b/src/components/createRetro/modal/StartDateCalender.tsx index 76e482a..ed83313 100644 --- a/src/components/createRetro/modal/StartDateCalender.tsx +++ b/src/components/createRetro/modal/StartDateCalender.tsx @@ -1,31 +1,20 @@ -import React, { useState } from 'react'; -import { ko } from 'date-fns/locale/ko'; +import React from 'react'; +import { Input } from '@chakra-ui/react'; import 'react-datepicker/dist/react-datepicker.css'; -import * as S from '@/styles/createRetro/modal/StartDateCalendar.style'; import '@/styles/createRetro/modal/Calendar.css'; interface StartDateCalendarProps { - onDateChange: (dateString: string) => void; + onDateChange: (date: Date) => void; } const StartDateCalendar: React.FC = ({ onDateChange }) => { - const [selectedDate, setSelectedDate] = useState(new Date()); - - const handleDateChange = (date: Date) => { - setSelectedDate(date); - const isoDateString = date.toISOString(); // 백엔드 request body에 보낼 날짜 타입 - onDateChange(isoDateString); + const handleChange = (event: React.ChangeEvent) => { + const dateString = event.target.value; // 사용자가 입력한 날짜 문자열 + const selectedDate = new Date(dateString); // 문자열을 Date 객체로 변환 + onDateChange(selectedDate); // 부모 컴포넌트로 전달 }; - return ( - - ); + return ; }; export default StartDateCalendar; diff --git a/src/components/inviteTeam/AcceptInvite.tsx b/src/components/inviteTeam/AcceptInvite.tsx new file mode 100644 index 0000000..bf8812b --- /dev/null +++ b/src/components/inviteTeam/AcceptInvite.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import postInviteTeam from '@/api/inviteTeamApi/postInviteTeam'; + +const AcceptInvite: React.FC = () => { + const { invitationId } = useParams<{ invitationId?: string }>(); + const navigate = useNavigate(); + const [inviteSuccess, setInviteSuccess] = useState(false); + + useEffect(() => { + const acceptInvitation = async () => { + try { + if (invitationId) { + await postInviteTeam(invitationId); + setInviteSuccess(true); // 초대 요청이 성공했을 때 상태를 true로 변경 + } else { + console.error('InvitationId 추출 실패'); + } + } catch (error) { + console.error('에러', error); + } + }; + + acceptInvitation(); + }, [invitationId]); + + useEffect(() => { + if (inviteSuccess) { + // 초대 성공 시 알림을 띄우고 retrolist 페이지로 navigate + alert('초대 성공했습니다!'); + navigate('/retrolist'); + } + }, [inviteSuccess, navigate]); + + return
초대를 수락하는 중...
; +}; + +export default AcceptInvite; diff --git a/src/components/inviteTeam/InviteTeam.tsx b/src/components/inviteTeam/InviteTeam.tsx deleted file mode 100644 index 11bc289..0000000 --- a/src/components/inviteTeam/InviteTeam.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import QRCode from 'qrcode.react'; -import { GetInviteTeamResponse, InviteTeamData } from '@/api/@types/InviteTeam'; -import getInviteTeam from '@/api/retrospectivesApi/getInviteTeam'; - -interface InviteTeamComponentProps { - teamId: number; -} - -const InviteTeamComponent: React.FC = ({ teamId }) => { - const [inviteData, setInviteData] = useState(null); - const [error, setError] = useState(''); - - useEffect(() => { - const fetchInviteData = async () => { - try { - const response: GetInviteTeamResponse = await getInviteTeam(teamId); - setInviteData(response.data); - } catch (error) { - console.error(error); - setError('팀원 초대 get 실패'); - } - }; - - fetchInviteData(); - }, [teamId]); // teamId가 변경될 때마다 호출 - - if (error) { - return
{error}
; - } - - if (!inviteData) { - return
로딩중...
; - } - - return ( -
-

초대 코드: {inviteData.invitationCode}

-

- 초대 URL:{' '} - - {inviteData.invitationUrl} - -

-

만료 시간: {inviteData.expirationTime}

- -

{inviteData.qrCodeImage}

-
- ); -}; - -export default InviteTeamComponent; diff --git a/src/components/inviteTeam/InviteTeamModal.tsx b/src/components/inviteTeam/InviteTeamModal.tsx index 796fb74..ad48fa7 100644 --- a/src/components/inviteTeam/InviteTeamModal.tsx +++ b/src/components/inviteTeam/InviteTeamModal.tsx @@ -19,7 +19,7 @@ import { } from '@chakra-ui/react'; import QRCode from 'qrcode.react'; import { GetInviteTeamResponse, InviteTeamData } from '@/api/@types/InviteTeam'; -import getInviteTeam from '@/api/retrospectivesApi/getInviteTeam'; +import getInviteTeam from '@/api/inviteTeamApi/getInviteTeam'; import * as S from '@/styles/inviteTeam/InviteTeamModal.style'; interface InviteTeamModalProps { diff --git a/src/components/layout/parts/MainNavBar.tsx b/src/components/layout/parts/MainNavBar.tsx index 5b68307..68a1ff9 100644 --- a/src/components/layout/parts/MainNavBar.tsx +++ b/src/components/layout/parts/MainNavBar.tsx @@ -28,7 +28,7 @@ const MainNavBar = () => { return ( <> -
+
+ + + + ); +}; + +export default ReviseCommentModal; diff --git a/src/components/writeRetro/task/ReviseModal.tsx b/src/components/writeRetro/task/ReviseModal.tsx index 0456824..08bee69 100644 --- a/src/components/writeRetro/task/ReviseModal.tsx +++ b/src/components/writeRetro/task/ReviseModal.tsx @@ -5,10 +5,10 @@ import { SectionServices } from '@/api/services/Section'; import * as S from '@/styles/writeRetroStyles/Layout.style'; interface Props { - name: sectionData; + section: sectionData; } -const ReviseModal: FC = ({ name }) => { +const ReviseModal: FC = ({ section }) => { // Input 높이 자동 조절 const [value, setValue] = useState(''); const handleChange = (e: ChangeEvent) => { @@ -19,7 +19,7 @@ const ReviseModal: FC = ({ name }) => { const ChangeContent = async () => { try { - const data = await SectionServices.patch({ sectionId: name.sectionId, sectionContent: value }); + const data = await SectionServices.patch({ sectionId: section.sectionId, sectionContent: value }); console.log(data); } catch (e) { console.error(e); @@ -34,7 +34,7 @@ const ReviseModal: FC = ({ name }) => { diff --git a/src/components/writeRetro/task/TeamTask.tsx b/src/components/writeRetro/task/TeamTask.tsx index 1ec68d5..edf61af 100644 --- a/src/components/writeRetro/task/TeamTask.tsx +++ b/src/components/writeRetro/task/TeamTask.tsx @@ -1,34 +1,53 @@ import { FC, useState } from 'react'; 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 { Flex, Popover, PopoverContent, PopoverTrigger } from '@chakra-ui/react'; -import dayjs from 'dayjs'; +import { useLocation } from 'react-router-dom'; +import { + Button, + Flex, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverHeader, + PopoverTrigger, + Portal, +} from '@chakra-ui/react'; +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'; -const formattedDate = (name: any) => dayjs(name).format('YYYY/MM/DD HH:MM'); - interface Props { - name: sectionData; + section: sectionData; } -const TeamTask: FC = ({ name }) => { +const TeamTask: FC = ({ section }) => { const toast = useCustomToast(); 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 { - const data = await SectionServices.likePost({ sectionId: name.sectionId }); + const data = await SectionServices.likePost({ sectionId: section.sectionId }); setLiked(data.data.likeCnt); + console.log('like', liked); } catch (e) { - console.error(e); + toast.error(e); } }; @@ -39,7 +58,7 @@ const TeamTask: FC = ({ name }) => { const DeleteSection = async () => { try { - await SectionServices.delete({ sectionId: name.sectionId }); + await SectionServices.delete({ sectionId: section.sectionId }); } catch (e) { toast.error(e); } @@ -53,34 +72,55 @@ const TeamTask: FC = ({ name }) => { - {name.username} + {section.username} - 삭제 + + + 삭제 + + + + + + + 삭제요청 + + + + 선택한 회고 카드를 삭제하시겠습니까? + + + + + + + {/* TaskCenter */} - {name.content} + {section.content} {/* (수정됨) */} + +
+ +
+ 담당자 +
{/* TaskTextModal */} - +
- {name.sectionName === 'Action Items' && ( - -
- M -
- {name.username} -
- )} {/* TaskBottom */} @@ -89,26 +129,26 @@ const TeamTask: FC = ({ name }) => { {liked ? : } - {name.likeCnt} + {section.likeCnt} {/* Message */} {messaged ? : } - 4 + {section.comments.length} {/* DaysLeft */} - {formattedDate(name.createdDate)} + {formattedDate(section.createdDate)} - {isVisible && } + {isVisible && } ); diff --git a/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx b/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx index ae9696d..ce2cc0a 100644 --- a/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx +++ b/src/components/writeRetro/task/taskMessage/TeamTaskMessage.tsx @@ -1,70 +1,124 @@ 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 { - name: sectionData; + section: sectionData; } -const TeamTaskMessage: FC = ({ name }) => { - const { isOpen, onOpen, onClose } = useDisclosure(); +const TeamTaskMessage: FC = ({ section }) => { 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 */} - 5개의 댓글 + {section.comments.length}개의 댓글 {/* TaskMessages */}
- {name.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 = ({ name }) => { {/* AddMessage */} - 확인 + 확인 diff --git a/src/constant/section.ts b/src/constant/section.ts index 5158fe9..16ba8bc 100644 --- a/src/constant/section.ts +++ b/src/constant/section.ts @@ -3,18 +3,37 @@ type sectionTitle = { title: string; }[]; -export const sectionTitleName: sectionTitle = [ +export const PersonalSectionTitleName: sectionTitle = [ { id: 1, - title: 'Keep', + title: '', }, { id: 2, - title: 'Problem', + title: '', }, { id: 3, - title: 'Try', + title: '', + }, + { + id: 4, + title: '', + }, +]; + +export const TeamSectionTitleName: sectionTitle = [ + { + id: 1, + title: 'Kudos', + }, + { + id: 2, + title: 'Went Well', + }, + { + id: 3, + title: 'To Improve', }, { id: 4, diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index a624581..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 @@ -82,4 +90,4 @@ export const TeamHandlers: RequestHandler[] = [ return HttpResponse.json(mockMembers); }), ]; -export const mswWorker = setupWorker(...TeamHandlers); +export const mswWorker = setupWorker(); diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 54fc33e..18fd153 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -20,7 +20,6 @@ const App: React.FC = () => {
- {/* */}
@@ -39,7 +38,6 @@ const App: React.FC = () => {
- {/*
*/} ); 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 d1cffb5..9dd30f6 100644 --- a/src/pages/RetroListPage.tsx +++ b/src/pages/RetroListPage.tsx @@ -1,87 +1,18 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { GetRetrospectiveRequest, GetRetrospectiveResponseNodes } from '@/api/@types/Retrospectives'; +import { GetRetrospectiveData } from '@/api/@types/Retrospectives'; +import { queryGetRetrospective } from '@/api/retrospectivesApi/getRetrospective'; +import BookmarkButton from '@/components/RetroList/BookmarkButton'; import ContentsFilter from '@/components/RetroList/ContentsFilter'; import ContentList from '@/components/RetroList/ContentsList'; -import ControlBar from '@/components/RetroList/ControlBar'; +import OrderButton from '@/components/RetroList/OrderButton'; +import Pagination from '@/components/RetroList/Pagination'; +import ProgressButton from '@/components/RetroList/ProgressButton'; import Search from '@/components/RetroList/Search'; import ViewButton from '@/components/RetroList/ViewButton'; +import { useCustomToast } from '@/hooks/useCustomToast'; import * as S from '@/styles/RetroList/RetroListPage.style'; -// 예시 데이터 -const dummy = { - code: 200, - message: null, - data: { - totalCount: 5, - nodes: [ - { - id: 1, - title: 'Sample Title 1', - userId: 100, - teamId: 200, // team - templateId: 0, - status: 'NOT_STARTED', - isBookmarked: false, - thumbnail: '', - startDate: '2024-04-10T09:22:51.538Z', - createdDate: '2024-04-10T09:22:51.538Z', - updatedDate: '2024-04-10T09:22:51.538Z', - }, - { - id: 2, - title: 'Sample Title 2', - userId: 101, - teamId: 300, // team - templateId: 0, - status: 'NOT_STARTED', - isBookmarked: true, - thumbnail: '', - startDate: '2024-04-11T09:22:51.538Z', - createdDate: '2024-04-11T09:22:51.538Z', - updatedDate: '2024-04-11T09:22:51.538Z', - }, - { - id: 3, - title: 'Sample Title 3', - userId: 102, - teamId: 400, // team - templateId: 0, - status: 'IN_PROGRESS', - isBookmarked: false, - thumbnail: 'https://example.com/thumbnail3.jpg', - startDate: '2024-04-12T09:22:51.538Z', - createdDate: '2024-04-12T09:22:51.538Z', - updatedDate: '2024-04-12T09:22:51.538Z', - }, - { - id: 4, - title: 'Sample Title 4', - userId: 103, - teamId: null, // personal - templateId: 0, - status: 'IN_PROGRESS', - isBookmarked: true, - thumbnail: 'https://example.com/thumbnail4.jpg', - startDate: '2024-04-13T09:22:51.538Z', - createdDate: '2024-04-13T09:22:51.538Z', - updatedDate: '2024-04-13T09:22:51.538Z', - }, - { - id: 5, - title: 'Sample Title 5', - userId: 104, - teamId: null, // personal - templateId: 0, - status: 'COMPLETED', - isBookmarked: false, - thumbnail: 'https://example.com/thumbnail5.jpg', - startDate: '2024-04-14T09:22:51.538Z', - createdDate: '2024-04-14T09:22:51.538Z', - updatedDate: '2024-04-14T09:22:51.538Z', - }, - ], - }, -}; - const formatDate = (isoDateString: string) => { const date = new Date(isoDateString); const year = date.getFullYear(); @@ -89,51 +20,152 @@ const formatDate = (isoDateString: string) => { const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); - return `${year}-${month}-${day} ${hours}:${minutes}`; }; const RetroListPage = () => { - // data 부분만 가져오기 - const { nodes } = dummy.data; + const [data, setData] = useState({ totalCount: 0, nodes: [] }); + const toast = useCustomToast(); + const [page, setPage] = useState(Math.ceil(data.totalCount / 10)); + const [currentPage, setCurrentPage] = useState(1); + const [bookmarkUpdate, setBookmarkUpdate] = useState(false); - const rawData = nodes.map(item => ({ - id: item.id, - title: item.title, - userId: item.userId, - teamId: item.teamId, - templateId: item.templateId, - status: item.status, - isBookmarked: item.isBookmarked, - thumbnail: item.thumbnail, - startDate: formatDate(item.startDate), - createdDate: formatDate(item.createdDate), - updatedDate: formatDate(item.updatedDate), - })); + const [query, setQuery] = useState({ + page: 0, + size: 10, + order: 'NEWEST', + status: 'NOT_STARTED', + keyword: '', + isBookmarked: false, + }); - // useEffect(() => { - // fetch(''); // url - // }); + useEffect(() => { + setPage(Math.ceil(data.totalCount / 10)); + }, [data]); - const [retroData, setRetroData] = useState(rawData); + useEffect(() => { + setQuery(prev => { + return { ...prev, page: currentPage - 1 }; + }); + }, [currentPage]); + + useEffect(() => { + const fetchRetrolist = async () => { + // console.log(query); + try { + const responseData = await queryGetRetrospective(query); + // console.log(query); + // console.log('회고 조회 성공'); + setData(responseData.data); + } catch (error) { + // console.error('회고 데이터를 가져오는 도중 오류가 발생했습니다:', error); + toast.error(error); + } + }; + fetchRetrolist(); + }, [query, bookmarkUpdate]); + + const [retroData, setRetroData] = useState>([]); const [viewMode, setViewMode] = useState('board'); const [searchData, setSearchData] = useState(''); + useEffect(() => { + // data 부분만 저장 + const rawData = data.nodes.map(item => ({ + id: item.id, + title: item.title, + userId: item.userId, + teamId: item.teamId, + templateId: item.templateId, + status: item.status, + isBookmarked: item.isBookmarked, + thumbnail: item.thumbnail, + startDate: formatDate(item.startDate), + createdDate: formatDate(item.createdDate), + updatedDate: formatDate(item.updatedDate), + username: item.username, + })); + setRetroData(rawData); + }, [data.nodes]); + + // console.log(retroData); const handleContentsFilter = (filterType: string) => { if (filterType === 'Personal') { - const filtered = rawData.filter(item => item.teamId === null); + const filtered = data.nodes + .filter(item => item.teamId === null) + .map(item => ({ + id: item.id, + title: item.title, + userId: item.userId, + teamId: item.teamId, + templateId: item.templateId, + status: item.status, + isBookmarked: item.isBookmarked, + thumbnail: item.thumbnail, + startDate: formatDate(item.startDate), + createdDate: formatDate(item.createdDate), + updatedDate: formatDate(item.updatedDate), + username: item.username, + })); setRetroData(filtered); } else if (filterType === 'Teams') { - const filtered = rawData.filter(item => item.teamId !== null); + const filtered = data.nodes + .filter(item => item.teamId !== null) + .map(item => ({ + id: item.id, + title: item.title, + userId: item.userId, + teamId: item.teamId, + templateId: item.templateId, + status: item.status, + isBookmarked: item.isBookmarked, + thumbnail: item.thumbnail, + startDate: formatDate(item.startDate), + createdDate: formatDate(item.createdDate), + updatedDate: formatDate(item.updatedDate), + username: item.username, + })); setRetroData(filtered); } else if (filterType === 'ALL') { + const rawData = data.nodes.map(item => ({ + id: item.id, + title: item.title, + userId: item.userId, + teamId: item.teamId, + templateId: item.templateId, + status: item.status, + isBookmarked: item.isBookmarked, + thumbnail: item.thumbnail, + startDate: formatDate(item.startDate), + createdDate: formatDate(item.createdDate), + updatedDate: formatDate(item.updatedDate), + username: item.username, + })); setRetroData(rawData); } }; + const handleStatus = (option: 'ALL' | 'NOT_STARTED' | 'IN_PROGRESS' | 'COMPLETED') => { + setQuery(prev => { + return { ...prev, status: option }; + }); + }; + const handleOrder = (option: 'NEWEST' | 'OLDEST') => { + setQuery(prev => { + return { ...prev, order: option }; + }); + }; + + const handleBookmarkButton = (option: boolean) => { + setQuery(prev => { + return { ...prev, isBookmarked: option }; + }); + }; + const handleViewModeChange = (newStatus: string) => { setViewMode(newStatus); }; + const handleSearch = (searchTerm: string) => { setSearchData(searchTerm); }; @@ -152,10 +184,22 @@ const RetroListPage = () => { - + + + + + - + + + +
); diff --git a/src/pages/RetroPersonalPage.tsx b/src/pages/RetroPersonalPage.tsx deleted file mode 100644 index 99b9d64..0000000 --- a/src/pages/RetroPersonalPage.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { useEffect, useState } from 'react'; -import { IoMdInformationCircle } from 'react-icons/io'; -import { useLocation } from 'react-router-dom'; -import { Flex } from '@chakra-ui/react'; -import { GetSectionResponse } from '@/api/@types/Section'; -import { mockSection } from '@/api/__mock__/section'; -import { SectionServices } from '@/api/services/Section'; -import { AddTask } from '@/components/writeRetro/layout/AddTask'; -import Label from '@/components/writeRetro/layout/Label'; -import Title from '@/components/writeRetro/layout/Title'; -import PersonalTask from '@/components/writeRetro/task/PersonalTask'; -import { sectionTitleName } from '@/constant/section'; -import { useCustomToast } from '@/hooks/useCustomToast'; -import * as S from '@/styles/writeRetroStyles/Layout.style'; - -const RetroPersonalPage = () => { - const [section, setSection] = useState(); - const toast = useCustomToast(); - const location = useLocation(); - console.log(location); - - const FetchSection = async () => { - try { - const data = await SectionServices.get({ - retrospectiveId: 1, - teamId: 2, - }); - if (!data) return; - if (data) { - setSection(data); - } - console.log('data', data.data); - } catch (e) { - toast.error(e); - } - }; - - useEffect(() => { - FetchSection(); - }, []); - - if (!section) return; - - return ( - - - <S.SectionBox> - <Flex flexDirection="column"> - <Flex> - <IoMdInformationCircle size={25} style={{ margin: 'auto 5px' }} /> - <p style={{ fontSize: '20px', margin: '5px' }}>수정을 원한다면, 해당 텍스트를 선택하세요!</p> - </Flex> - <Flex> - {sectionTitleName.map(title => ( - <S.FrameStyle> - <Label - labelName={title.title} - labelType="dark" - taskCount={mockSection.data.filter(data => data.sectionName === title.title).length} - /> - {mockSection.data - .filter(key => key.sectionName === title.title) - .map(name => ( - <PersonalTask name={name} /> - ))} - <AddTask color="dark" /> - </S.FrameStyle> - ))} - </Flex> - </Flex> - </S.SectionBox> - </S.Container> - ); -}; - -export default RetroPersonalPage; diff --git a/src/pages/RetroTeamPage.tsx b/src/pages/RetroTeamPage.tsx deleted file mode 100644 index 9275911..0000000 --- a/src/pages/RetroTeamPage.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useEffect, useState } from 'react'; -import { IoMdInformationCircle } from 'react-icons/io'; -import { Flex } from '@chakra-ui/react'; -import { sectionData } from '@/api/@types/Section'; -import { mockSection } from '@/api/__mock__/section'; -import { SectionServices } from '@/api/services/Section'; -import { AddTask } from '@/components/writeRetro/layout/AddTask'; -import Label from '@/components/writeRetro/layout/Label'; -import Title from '@/components/writeRetro/layout/Title'; -import TeamTask from '@/components/writeRetro/task/TeamTask'; -import { sectionTitleName } from '@/constant/section'; -import { useCustomToast } from '@/hooks/useCustomToast'; -import * as S from '@/styles/writeRetroStyles/Layout.style'; - -const RetroTeamPage = () => { - const [section, setSection] = useState<sectionData[]>(); - const toast = useCustomToast(); - - const FetchSection = async () => { - try { - const data = await SectionServices.get({ retrospectiveId: 57, teamId: 31 }); - setSection(data.data); - console.log('section', section); - } catch (e) { - toast.error(e); - } - }; - - useEffect(() => { - FetchSection(); - }, []); - - return ( - <S.Container> - <Title /> - <S.SectionBox> - <Flex flexDirection="column" margin="0 auto"> - <Flex> - <IoMdInformationCircle size={25} style={{ margin: 'auto 5px' }} /> - <p style={{ fontSize: '20px', margin: '5px' }}>수정을 원한다면, 해당 텍스트를 선택하세요!</p> - </Flex> - <Flex> - {sectionTitleName.map(title => ( - <S.FrameStyle> - <Label - labelName={title.title} - labelType="dark" - taskCount={mockSection.data.filter(data => data.sectionName === title.title).length} - /> - {mockSection.data - .filter(key => key.sectionName === title.title) - .map(name => ( - <TeamTask name={name} /> - ))} - <AddTask color="dark" /> - </S.FrameStyle> - ))} - </Flex> - </Flex> - </S.SectionBox> - </S.Container> - ); -}; - -export default RetroTeamPage; diff --git a/src/pages/RevisePage.tsx b/src/pages/RevisePage.tsx index eb117ff..2738a72 100644 --- a/src/pages/RevisePage.tsx +++ b/src/pages/RevisePage.tsx @@ -3,7 +3,9 @@ import { MdPeopleAlt } from 'react-icons/md'; import { useLocation } from 'react-router-dom'; import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react'; import { RetrospectiveData } from '@/api/@types/Retrospectives'; +import { TeamMembersData } from '@/api/@types/TeamController'; import { RetrospectiveService } from '@/api/services/Retrospectives'; +import { TeamControllerServices } from '@/api/services/TeamController'; import ManageTeamMembers from '@/components/writeRetro/revise/ManageTeamMembers'; import NotTeamMemberModal from '@/components/writeRetro/revise/NotTeamMemberModal'; import ReviseSetting from '@/components/writeRetro/revise/ReviseSetting'; @@ -16,16 +18,30 @@ const RetroRevisePage = () => { const query = search.split(/[=,&]/); const retrospectiveId = Number(query[1]); const teamId = Number(query[3]); - const [retro, setRetro] = useState<RetrospectiveData>(); + const [members, setMembers] = useState<TeamMembersData[]>(); + const [status, setStatus] = useState<string>('NOT_STARTED'); const toast = useCustomToast(); const FetchRetrospective = async () => { try { const data = await RetrospectiveService.onlyGet({ retrospectiveId: retrospectiveId }); - console.log('data56', data); setRetro(data.data); - console.log('retro', retro); + if (retro) { + setStatus(retro.status); + } + } catch (e) { + toast.error(e); + } + }; + + const fetchTeamMembers = async () => { + try { + if (teamId) { + const data = await TeamControllerServices.TeamMemberGet({ teamId: teamId, retrospectiveId: retrospectiveId }); + setMembers(data.data); + } + return; } catch (e) { toast.error(e); } @@ -33,6 +49,7 @@ const RetroRevisePage = () => { useEffect(() => { FetchRetrospective(); + fetchTeamMembers(); }, [retro?.status]); if (!retro) return; @@ -45,24 +62,19 @@ const RetroRevisePage = () => { <S.TitleText>FistRetro</S.TitleText> </Flex> </S.TitleBox> - {/* <SettingMenu></SettingMenu> */} <S.SettingMenuStyle> <Tabs colorScheme="brand" isLazy isFitted> <TabList margin="0 40px" fontSize={60}> <Tab>회고 설정</Tab> - {retro.teamId ? <Tab>팀원 관리</Tab> : null} + {retro.type === 'TEAM' ? <Tab>팀원 관리</Tab> : null} </TabList> <TabPanels> <TabPanel> - <ReviseSetting /> + <ReviseSetting retro={retro} status={status} setStatus={setStatus} /> </TabPanel> <TabPanel> - {retro.teamId ? ( - <ManageTeamMembers teamId={teamId} retrospectiveId={retrospectiveId} /> - ) : ( - <NotTeamMemberModal /> - )} + {members ? <ManageTeamMembers members={members} teamId={teamId} /> : <NotTeamMemberModal />} </TabPanel> </TabPanels> </Tabs> diff --git a/src/pages/SectionPage.tsx b/src/pages/SectionPage.tsx new file mode 100644 index 0000000..4327402 --- /dev/null +++ b/src/pages/SectionPage.tsx @@ -0,0 +1,119 @@ +import { useEffect, useState } from 'react'; +import { IoMdInformationCircle } from 'react-icons/io'; +import { useLocation } from 'react-router-dom'; +import { Flex } from '@chakra-ui/react'; +import { RetrospectiveData } from '@/api/@types/Retrospectives'; +import { sectionData } from '@/api/@types/Section'; +import { TemplateNameData } from '@/api/@types/TeamController'; +import { RetrospectiveService } from '@/api/services/Retrospectives'; +import { SectionServices } from '@/api/services/Section'; +import { TeamControllerServices } from '@/api/services/TeamController'; +import { AddTask } from '@/components/writeRetro/layout/AddTask'; +import Label from '@/components/writeRetro/layout/Label'; +import Title from '@/components/writeRetro/layout/Title'; +import TeamTask from '@/components/writeRetro/task/TeamTask'; +import { PersonalSectionTitleName } from '@/constant/section'; +import { useCustomToast } from '@/hooks/useCustomToast'; +import * as S from '@/styles/writeRetroStyles/Layout.style'; + +const RetroTeamPage = () => { + const { search } = useLocation(); + const query = search.split(/[=,&]/); + const retrospectiveId = Number(query[1]); + const teamId = Number(query[3]); + const [section, setSection] = useState<sectionData[]>([]); + const [retro, setRetro] = useState<RetrospectiveData>(); + const [template, setTemplate] = useState<TemplateNameData[]>(); + const toast = useCustomToast(); + + const fetchRetrospective = async () => { + try { + const data = await RetrospectiveService.onlyGet({ retrospectiveId: retrospectiveId }); + console.log('retro.data', data.data); + setRetro(data.data); + console.log('retro', retro); + } catch (e) { + toast.error(e); + } + }; + + const fetchSection = async () => { + try { + if (!teamId) { + const data = await SectionServices.PersonalGet({ retrospectiveId: retrospectiveId }); + setSection(data.data); + } else { + const data = await SectionServices.TeamGet({ retrospectiveId: retrospectiveId, teamId: teamId }); + setSection(data.data); + } + } catch (e) { + toast.error(e); + } + }; + + const fetchTemplate = async () => { + try { + if (retro) { + const data = await TeamControllerServices.TemplateNameGet({ templateId: retro.templateId }); + setTemplate(data.data); + console.log('template', template); + } + } catch (e) { + toast.error(e); + } + }; + + useEffect(() => { + fetchSection(); + fetchRetrospective(); + fetchTemplate(); + }, [retro?.status, template?.values]); + + return ( + <S.Container> + <Title /> + <S.SectionBox> + <Flex flexDirection="column" margin="0 auto"> + <Flex> + <IoMdInformationCircle size={25} style={{ margin: 'auto 5px' }} /> + <p style={{ fontSize: '20px', margin: '5px' }}>수정을 원한다면, 해당 텍스트를 선택하세요!</p> + </Flex> + <Flex> + {template + ? template.map(title => ( + <> + <S.FrameStyle> + <Label + labelName={title.name} + labelType="dark" + taskCount={section.filter(data => data.sectionName === title.name).length} + /> + + <AddTask template={title.id} retrospectiveId={retro?.retrospectiveId} /> + {section + .filter(key => key.sectionName === title.name) + .map(section => ( + <> + <TeamTask section={section} /> + </> + ))} + </S.FrameStyle> + </> + )) + : PersonalSectionTitleName.map(title => ( + <S.FrameStyle> + <Label + labelName={title.title} + labelType="dark" + taskCount={section.filter(data => data.sectionName === title.title).length} + /> + </S.FrameStyle> + ))} + </Flex> + </Flex> + </S.SectionBox> + </S.Container> + ); +}; + +export default RetroTeamPage; diff --git a/src/pages/SurveyPage.tsx b/src/pages/SurveyPage.tsx index 4cf9f43..493287d 100644 --- a/src/pages/SurveyPage.tsx +++ b/src/pages/SurveyPage.tsx @@ -1,4 +1,5 @@ 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'; @@ -15,6 +16,7 @@ const SurveyPage: React.FC = () => { localStorage.setItem('surveyVisited', 'true'); }, []); + const navigate = useNavigate(); const handleSurveyButtonClick = () => { @@ -23,6 +25,7 @@ const SurveyPage: React.FC = () => { const handleSurvey = async () => { try { + console.log( '나이는:', age, @@ -36,34 +39,31 @@ 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<string>(''); + const [gender, setGender] = useState<string>('FEMALE'); const [job, setJob] = useState<string>(''); const [city, setCity] = useState<string>('서울'); const [path, setPath] = useState<string>(''); const [purpose, setPurpose] = useState<string[]>(); + const handleAgeChange = (age: string) => { setAge(age); }; + const numAge: number = parseInt(age, 10); + const handleGenderChange = (gender: string) => { setGender(gender); }; @@ -76,10 +76,12 @@ const SurveyPage: React.FC = () => { const handlePathChange = (path: string) => { setPath(path); }; + const handlePurposeChange = (purpose: string[]) => { setPurpose(purpose); }; + return ( <> <S.Background> @@ -97,7 +99,9 @@ const SurveyPage: React.FC = () => { <Divider /> <PathRadio onPathChange={handlePathChange} /> <Divider /> + <PurposeCheckbox onPurposeChange={handlePurposeChange} /> + <Button onClick={handleSurveyButtonClick} colorScheme="brand" width="80%" style={{ marginBottom: '4rem' }}> 제출 </Button> diff --git a/src/styles/Main/Template1.styles.ts b/src/styles/Main/Template1.styles.ts index 7693292..f5a971f 100644 --- a/src/styles/Main/Template1.styles.ts +++ b/src/styles/Main/Template1.styles.ts @@ -4,7 +4,7 @@ export const Container = styled.div` height: 100vh; display: grid; grid-template-columns: repeat(5, 1fr); - grid-template-rows: 120px 30px 60px auto; + grid-template-rows: 90px 30px 60px auto; overflow-x: hidden; padding-left: 115px; padding-right: 115px; diff --git a/src/styles/Main/Template4.styles.ts b/src/styles/Main/Template4.styles.ts index 5fae4f1..169bbf3 100644 --- a/src/styles/Main/Template4.styles.ts +++ b/src/styles/Main/Template4.styles.ts @@ -77,7 +77,8 @@ export const DotContainer = styled.div` transform: translateX(-50%); display: flex; z-index: 2; - top: 3%; + top: 3.5%; + left: 50%; `; export const Dot = styled.div<{ active: boolean }>` diff --git a/src/styles/RetroList/BookmarkButton.styles.ts b/src/styles/RetroList/BookmarkButton.styles.ts index 543461a..0be6b84 100644 --- a/src/styles/RetroList/BookmarkButton.styles.ts +++ b/src/styles/RetroList/BookmarkButton.styles.ts @@ -1,3 +1,4 @@ +import { FaStar } from 'react-icons/fa'; import styled from 'styled-components'; interface ButtonProps { @@ -27,3 +28,11 @@ export const Text = styled.text` justify-self: start; align-self: center; `; + +export const StarIcon = styled(FaStar)` + width: 15px; + height: 15px; + justify-self: center; + align-self: center; + color: #fcea12; +`; diff --git a/src/styles/RetroList/ContentsList.styles.ts b/src/styles/RetroList/ContentsList.styles.ts index 96df750..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` @@ -53,6 +56,8 @@ export const RetroDate = styled.span` export const Thumbnail = styled.img` width: 100%; height: 100%; + object-fit: cover; + aspect-ratio: 1/1; `; export const TeamIcon = styled.img` @@ -85,15 +90,9 @@ export const BookmarkIcon = styled.img` } `; -export const ProgressIcon = styled.img` - align-self: start; - justify-self: end; - width: 15px; - height: 15px; -`; - export const ListContainer = styled.div` padding-top: 10px; + padding-bottom: 20px; `; export const ListTopBox = styled.div` @@ -111,6 +110,9 @@ export const ItemBox = styled.li` export const ListTitleBox = styled.div` flex: 1; + &:hover { + cursor: pointer; + } `; export const ListUserBox = styled.div` flex: 1; @@ -131,4 +133,24 @@ export const ListProgressBox = styled.div` export const Icon = styled.img` width: 20px; height: 20px; + &:hover { + 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; + } `; diff --git a/src/styles/RetroList/ControlBar.styles.ts b/src/styles/RetroList/ControlBar.styles.ts deleted file mode 100644 index 73fa972..0000000 --- a/src/styles/RetroList/ControlBar.styles.ts +++ /dev/null @@ -1,8 +0,0 @@ -import styled from 'styled-components'; - -export const Container = styled.div` - margin-left: 50px; - margin-right: 50px; - margin-top: 15px; - display: flex; -`; diff --git a/src/styles/RetroList/Modal.styles.ts b/src/styles/RetroList/Modal.styles.ts index 3388ebd..a027fba 100644 --- a/src/styles/RetroList/Modal.styles.ts +++ b/src/styles/RetroList/Modal.styles.ts @@ -10,6 +10,7 @@ export const ModalOverlay = styled.div<{ isOpen: boolean }>` display: ${props => (props.isOpen ? 'flex' : 'none')}; justify-content: center; align-items: center; + z-index: 999; `; export const ModalContent = styled.div` diff --git a/src/styles/RetroList/Pagination.styles.ts b/src/styles/RetroList/Pagination.styles.ts new file mode 100644 index 0000000..3d8196d --- /dev/null +++ b/src/styles/RetroList/Pagination.styles.ts @@ -0,0 +1,49 @@ +import styled from 'styled-components'; +export const Conatiner = styled.div` + position: absolute; + bottom: 0; + width: 100%; + padding-bottom: 10px; +`; +export const ButtonContainer = styled.div` + margin-top: 20px; + display: flex; + flex-direction: row; + justify-content: center; +`; +export const LeftArrow = styled.span` + background-color: #f0f0f0; + margin-right: 10px; + font-weight: bold; + font-size: large; + color: #888888; + padding: 3px; + border-radius: 3px; +`; + +export const RightArrow = styled.span` + background-color: #f0f0f0; + margin-left: 10px; + font-weight: bold; + font-size: large; + color: #888888; + padding: 3px; + border-radius: 3px; +`; + +export const PageNumberContainer = styled.div` + margin-left: 10px; + margin-right: 10px; + display: inline; +`; + +export const PageNumber = styled.button<{ active?: boolean }>` + font-size: large; + font-weight: bold; + color: ${({ active }) => (active ? '#111b47' : '#c3c3c3')}; + + &:active, + &:focus { + outline: none; + } +`; diff --git a/src/styles/RetroList/RetroListPage.style.ts b/src/styles/RetroList/RetroListPage.style.ts index 1c2ee02..d310e7e 100644 --- a/src/styles/RetroList/RetroListPage.style.ts +++ b/src/styles/RetroList/RetroListPage.style.ts @@ -27,4 +27,18 @@ export const SortButtonContainer = styled.div` export const Box = styled.div` padding-left: 40px; padding-right: 40px; + margin: 0 auto; + width: 1000px; +`; + +export const ControlBarContainer = styled.div` + margin-left: 50px; + margin-right: 50px; + margin-top: 15px; + display: flex; +`; + +export const PageContainer = styled.div` + margin-top: 40px; + margin-bottom: 20px; `; diff --git a/src/styles/layout/layout.style.ts b/src/styles/layout/layout.style.ts index c61bc8f..eeb5d4a 100644 --- a/src/styles/layout/layout.style.ts +++ b/src/styles/layout/layout.style.ts @@ -143,6 +143,7 @@ export const GetStaredButton = styled.button` export const MenuText = styled.a` color: #111b47; + font-weight: 600; text-decoration: none; `; diff --git a/src/styles/writeRetroStyles/Layout.style.ts b/src/styles/writeRetroStyles/Layout.style.ts index bdf0b91..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; @@ -414,6 +410,7 @@ export const ManagerButton = styled.button` &:hover { cursor: pointer; } + position: relative; `; export const ReviseModalStyle = styled.div` @@ -479,3 +476,21 @@ export const ReviseModalButton = styled.button` border-radius: 4px; margin-right: 30px; `; + +export const HoverUser = styled.span` + position: absolute; + width: 40px; + left: -5px; /* 원하는 만큼의 왼쪽 이동 값 설정 */ + top: 100%; +`; + +export const ActionItemsUserImg = styled.img` + width: 24px; + height: 24px; +`; + +export const DeleteSectionText = styled.p` + margin: 20px; + color: #111b47; + font-size: 15px; +`; diff --git a/src/styles/writeRetroStyles/Members.styles.ts b/src/styles/writeRetroStyles/Members.styles.ts new file mode 100644 index 0000000..71d36ce --- /dev/null +++ b/src/styles/writeRetroStyles/Members.styles.ts @@ -0,0 +1,49 @@ +import styled from 'styled-components'; + +export const ListConatiner = styled.div` + position: absolute; + z-index: 1; + width: 280px; + max-height: 350px; + overflow-y: auto; + border: 1px solid #c8c8c8; + border-radius: 10px; + background-color: #f4f4f4; +`; + +export const Title = styled.span` + color: #969696; +`; + +export const TitleContainer = styled.div` + background-color: #f0f0f0; + border-top-right-radius: 10px; + border-top-left-radius: 10px; + border-bottom: 1px solid #e5e5e5; + padding: 10px; +`; + +export const ListBox = styled.ul``; + +export const ListItem = styled.li` + list-style: none; + display: flex; + flex-direction: row; + margin-top: 10px; + margin-bottom: 10px; + color: #969696; + &:hover { + cursor: pointer; + } +`; + +export const ProfileImage = styled.img` + margin-left: 5px; + margin-right: 5px; + width: 30px; + height: 30px; +`; + +export const UserName = styled.span` + padding-left: 10px; +`; diff --git a/src/styles/writeRetroStyles/ReviseLayout.style.ts b/src/styles/writeRetroStyles/ReviseLayout.style.ts index 402b00c..1e94c2d 100644 --- a/src/styles/writeRetroStyles/ReviseLayout.style.ts +++ b/src/styles/writeRetroStyles/ReviseLayout.style.ts @@ -370,3 +370,8 @@ export const SettingDetailText = styled.p` font-size: 15px; margin: auto 5px; `; +export const NotMemberInfo = styled.p` + font-size: 20px; + color: #636363; + margin: auto 0; +`; diff --git a/yarn.lock b/yarn.lock index c66fe10..e0916ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9565,10 +9565,10 @@ react-hook-form@^7.43.5: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.2.tgz" integrity sha512-y++lwaWjtzDt/XNnyGDQy6goHskFualmDlf+jzEZvjvz6KWDf7EboL7pUvRCzPTJd0EOPpdekYaQLEvvG6m6HA== -react-icons@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz" - integrity sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw== +react-icons@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.1.0.tgz#9e7533cc256571a610c2a1ec8a7a143fb1222943" + integrity sha512-D3zug1270S4hbSlIRJ0CUS97QE1yNNKDjzQe3HqY0aefp2CBn9VgzgES27sRR2gOvFK+0CNx/BW0ggOESp6fqQ== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -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==