Feat: 리뷰 관련 훅, 쿼리 구현
This commit is contained in:
parent
04755eb526
commit
2cb13477fb
60
frontend/src/api/reviewApi.ts
Normal file
60
frontend/src/api/reviewApi.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import api from '@/api/axiosConfig';
|
||||||
|
import { ReviewDetailResponse, ReviewRequest, ReviewResponse } from '@/types';
|
||||||
|
|
||||||
|
// 리뷰 단건 조회
|
||||||
|
export async function getReviewDetail(projectId: number, reviewId: number, memberId: number) {
|
||||||
|
return api
|
||||||
|
.get<ReviewDetailResponse>(`/projects/${projectId}/reviews/${reviewId}`, {
|
||||||
|
params: { memberId },
|
||||||
|
})
|
||||||
|
.then(({ data }) => data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 리뷰 생성
|
||||||
|
export async function createReview(projectId: number, memberId: number, reviewData: ReviewRequest) {
|
||||||
|
return api
|
||||||
|
.post<ReviewResponse>(`/projects/${projectId}/reviews`, reviewData, {
|
||||||
|
params: { memberId },
|
||||||
|
})
|
||||||
|
.then(({ data }) => data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 리뷰 수정
|
||||||
|
export async function updateReview(projectId: number, reviewId: number, memberId: number, reviewData: ReviewRequest) {
|
||||||
|
return api
|
||||||
|
.put<ReviewResponse>(`/projects/${projectId}/reviews/${reviewId}`, reviewData, {
|
||||||
|
params: { memberId },
|
||||||
|
})
|
||||||
|
.then(({ data }) => data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 리뷰 삭제
|
||||||
|
export async function deleteReview(projectId: number, reviewId: number, memberId: number) {
|
||||||
|
return api
|
||||||
|
.delete(`/projects/${projectId}/reviews/${reviewId}`, {
|
||||||
|
params: { memberId },
|
||||||
|
})
|
||||||
|
.then(({ data }) => data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 리뷰 상태 변경
|
||||||
|
export async function updateReviewStatus(projectId: number, reviewId: number, memberId: number, reviewStatus: string) {
|
||||||
|
return api
|
||||||
|
.put<ReviewResponse>(
|
||||||
|
`/projects/${projectId}/reviews/${reviewId}/status`,
|
||||||
|
{ reviewStatus },
|
||||||
|
{
|
||||||
|
params: { memberId },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(({ data }) => data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 리뷰 상태별 조회
|
||||||
|
export async function getReviewByStatus(projectId: number, memberId: number, reviewStatus: string) {
|
||||||
|
return api
|
||||||
|
.get<ReviewResponse[]>(`/projects/${projectId}/reviews`, {
|
||||||
|
params: { memberId, reviewStatus },
|
||||||
|
})
|
||||||
|
.then(({ data }) => data);
|
||||||
|
}
|
57
frontend/src/hooks/useReviewHooks.ts
Normal file
57
frontend/src/hooks/useReviewHooks.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { createReview, updateReview, deleteReview, updateReviewStatus } from '@/api/reviewApi';
|
||||||
|
import { ReviewRequest, ReviewResponse } from '@/types';
|
||||||
|
|
||||||
|
// 리뷰 생성 훅
|
||||||
|
export const useCreateReview = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<ReviewResponse, Error, { projectId: number; memberId: number; reviewData: ReviewRequest }>({
|
||||||
|
mutationFn: ({ projectId, memberId, reviewData }) => createReview(projectId, memberId, reviewData),
|
||||||
|
onSuccess: (_, { projectId, memberId }) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['reviewList', projectId, memberId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 리뷰 수정 훅
|
||||||
|
export const useUpdateReview = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<
|
||||||
|
ReviewResponse,
|
||||||
|
Error,
|
||||||
|
{ projectId: number; reviewId: number; memberId: number; reviewData: ReviewRequest }
|
||||||
|
>({
|
||||||
|
mutationFn: ({ projectId, reviewId, memberId, reviewData }) =>
|
||||||
|
updateReview(projectId, reviewId, memberId, reviewData),
|
||||||
|
onSuccess: (_, { projectId, reviewId }) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['reviewDetail', projectId, reviewId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 리뷰 삭제 훅
|
||||||
|
export const useDeleteReview = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<void, Error, { projectId: number; reviewId: number; memberId: number }>({
|
||||||
|
mutationFn: ({ projectId, reviewId, memberId }) => deleteReview(projectId, reviewId, memberId),
|
||||||
|
onSuccess: (_, { projectId, reviewId }) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['reviewDetail', projectId, reviewId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 리뷰 상태 변경 훅
|
||||||
|
export const useUpdateReviewStatus = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<
|
||||||
|
ReviewResponse,
|
||||||
|
Error,
|
||||||
|
{ projectId: number; reviewId: number; memberId: number; reviewStatus: string }
|
||||||
|
>({
|
||||||
|
mutationFn: ({ projectId, reviewId, memberId, reviewStatus }) =>
|
||||||
|
updateReviewStatus(projectId, reviewId, memberId, reviewStatus),
|
||||||
|
onSuccess: (_, { projectId, reviewId }) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['reviewDetail', projectId, reviewId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@ -155,6 +155,93 @@ export const handlers = [
|
|||||||
return HttpResponse.json({});
|
return HttpResponse.json({});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
http.post('/api/projects/:projectId/label/auto', () => {
|
||||||
|
const response: AutoLabelingResponse = {
|
||||||
|
imageId: 1,
|
||||||
|
imageUrl: 'image-url.jpg',
|
||||||
|
data: `{
|
||||||
|
"version": "0.1.0",
|
||||||
|
"task_type": "cls",
|
||||||
|
"shapes": [
|
||||||
|
{
|
||||||
|
"label": "NG",
|
||||||
|
"color": "#FF0000",
|
||||||
|
"points": [[0, 0]],
|
||||||
|
"group_id": null,
|
||||||
|
"shape_type": "point",
|
||||||
|
"flags": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"split": "none",
|
||||||
|
"imageHeight": 2000,
|
||||||
|
"imageWidth": 4000,
|
||||||
|
"imageDepth": 4
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
return HttpResponse.json(response);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// DELETE: 프로젝트 멤버 제거 핸들러
|
||||||
|
http.delete('/api/projects/:projectId/members', ({ params }) => {
|
||||||
|
const { projectId } = params;
|
||||||
|
|
||||||
|
return HttpResponse.json({ message: `프로젝트 ${projectId}에서 멤버 제거 성공` });
|
||||||
|
}),
|
||||||
|
// PUT: 프로젝트 멤버 권한 수정 핸들러
|
||||||
|
http.put('/api/projects/:projectId/members', () => {
|
||||||
|
return HttpResponse.json({});
|
||||||
|
}),
|
||||||
|
// POST: 워크스페이스 멤버 추가 핸들러
|
||||||
|
http.post('/api/workspaces/:workspaceId/members/:memberId', ({ params }) => {
|
||||||
|
const { workspaceId, memberId } = params;
|
||||||
|
|
||||||
|
if (!workspaceId || !memberId) {
|
||||||
|
const errorResponse: ErrorResponse = {
|
||||||
|
status: 400,
|
||||||
|
code: 1002,
|
||||||
|
message: '잘못된 요청입니다. 요청을 확인해주세요.',
|
||||||
|
isSuccess: false,
|
||||||
|
};
|
||||||
|
return HttpResponse.json(errorResponse, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 성공 응답
|
||||||
|
const response: WorkspaceResponse = {
|
||||||
|
id: parseInt(workspaceId as string, 10),
|
||||||
|
memberId: parseInt(memberId as string, 10),
|
||||||
|
title: 'Workspace 1',
|
||||||
|
content: 'Workspace for testing',
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse.json(response, { status: 200 });
|
||||||
|
}),
|
||||||
|
|
||||||
|
// GET: 프로젝트 멤버 리스트 조회 핸들러 (가상)
|
||||||
|
// 실제 구현 시 API 경로와 메서드를 확인 후 업데이트 필요
|
||||||
|
http.get('/api/projects/:projectId/members', () => {
|
||||||
|
const members: MemberResponse[] = [
|
||||||
|
{ id: 1, nickname: 'admin', profileImage: 'admin.jpg' },
|
||||||
|
{ id: 2, nickname: 'editor', profileImage: 'editor.jpg' },
|
||||||
|
{ id: 3, nickname: 'viewer', profileImage: 'viewer.jpg' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return HttpResponse.json(members);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// GET: 워크스페이스 멤버 리스트 조회 핸들러 (가상)
|
||||||
|
// 실제 구현 시 API 경로와 메서드를 확인 후 업데이트 필요
|
||||||
|
http.get('/api/workspaces/:workspaceId/members', () => {
|
||||||
|
const members: MemberResponse[] = [
|
||||||
|
{ id: 1, nickname: 'admin', profileImage: 'admin.jpg' },
|
||||||
|
{ id: 2, nickname: 'editor', profileImage: 'editor.jpg' },
|
||||||
|
{ id: 3, nickname: 'viewer', profileImage: 'viewer.jpg' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return HttpResponse.json(members);
|
||||||
|
}),
|
||||||
|
|
||||||
// Folder and Image Handlers
|
// Folder and Image Handlers
|
||||||
http.get('/api/projects/:projectId/folders/:folderId', ({ params }) => {
|
http.get('/api/projects/:projectId/folders/:folderId', ({ params }) => {
|
||||||
const { folderId } = params;
|
const { folderId } = params;
|
||||||
|
9
frontend/src/queries/useReviewByStatusQuery.ts
Normal file
9
frontend/src/queries/useReviewByStatusQuery.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { getReviewByStatus } from '@/api/reviewApi';
|
||||||
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export default function useReviewByStatusQuery(projectId: number, memberId: number, reviewStatus: string) {
|
||||||
|
return useSuspenseQuery({
|
||||||
|
queryKey: ['reviewByStatus', projectId, reviewStatus],
|
||||||
|
queryFn: () => getReviewByStatus(projectId, memberId, reviewStatus),
|
||||||
|
});
|
||||||
|
}
|
9
frontend/src/queries/useReviewDetailQuery.ts
Normal file
9
frontend/src/queries/useReviewDetailQuery.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { getReviewDetail } from '@/api/reviewApi';
|
||||||
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export default function useReviewDetailQuery(projectId: number, reviewId: number, memberId: number) {
|
||||||
|
return useSuspenseQuery({
|
||||||
|
queryKey: ['reviewDetail', projectId, reviewId, memberId],
|
||||||
|
queryFn: () => getReviewDetail(projectId, reviewId, memberId),
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user