diff --git a/frontend/src/assets/icons/chevron-down.svg b/frontend/src/assets/icons/chevron-down.svg
new file mode 100644
index 0000000..1b67915
--- /dev/null
+++ b/frontend/src/assets/icons/chevron-down.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/frontend/src/assets/icons/chevron-up.svg b/frontend/src/assets/icons/chevron-up.svg
new file mode 100644
index 0000000..c081caa
--- /dev/null
+++ b/frontend/src/assets/icons/chevron-up.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/frontend/src/assets/icons/delete.svg b/frontend/src/assets/icons/delete.svg
new file mode 100644
index 0000000..af61484
--- /dev/null
+++ b/frontend/src/assets/icons/delete.svg
@@ -0,0 +1,19 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/CanvasControlBar/index.tsx b/frontend/src/components/CanvasControlBar/index.tsx
index 987b288..d9ef705 100644
--- a/frontend/src/components/CanvasControlBar/index.tsx
+++ b/frontend/src/components/CanvasControlBar/index.tsx
@@ -1,6 +1,7 @@
import useCanvasStore from '@/stores/useCanvasStore';
import { LucideIcon, MousePointer2, PenTool, Plus, Save, Square } from 'lucide-react';
import { cn } from '@/lib/utils';
+import { MessageSquare } from 'lucide-react';
export default function CanvasControlBar({
saveJson,
@@ -10,13 +11,14 @@ export default function CanvasControlBar({
projectType: 'classification' | 'detection' | 'segmentation';
}) {
const { drawState, setDrawState, setLabels, labels } = useCanvasStore();
- const buttonBaseClassName = 'rounded-lg p-2 transition-colors ';
+ const buttonBaseClassName = 'rounded-lg p-2 transition-colors';
const buttonClassName = 'hover:bg-gray-100';
const activeButtonClassName = 'bg-primary stroke-white';
const controls: { [key: string]: LucideIcon } = {
pointer: MousePointer2,
...(projectType === 'segmentation' ? { pen: PenTool } : projectType === 'detection' ? { rect: Square } : null),
+ comment: MessageSquare,
};
return (
@@ -36,6 +38,7 @@ export default function CanvasControlBar({
);
})}
+
{projectType === 'classification' && labels.length === 0 && (
)}
+
+
>
)}
diff --git a/frontend/src/components/ui/spinner.tsx b/frontend/src/components/ui/spinner.tsx
new file mode 100644
index 0000000..7b319f5
--- /dev/null
+++ b/frontend/src/components/ui/spinner.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { cn } from '@/lib/utils';
+import { VariantProps, cva } from 'class-variance-authority';
+import { Loader2 } from 'lucide-react';
+
+const spinnerVariants = cva('flex-col items-center justify-center', {
+ variants: {
+ show: {
+ true: 'flex',
+ false: 'hidden',
+ },
+ },
+ defaultVariants: {
+ show: true,
+ },
+});
+
+const loaderVariants = cva('animate-spin text-primary', {
+ variants: {
+ size: {
+ small: 'size-6',
+ medium: 'size-8',
+ large: 'size-12',
+ },
+ },
+ defaultVariants: {
+ size: 'medium',
+ },
+});
+
+interface SpinnerContentProps extends VariantProps, VariantProps {
+ className?: string;
+ children?: React.ReactNode;
+}
+
+export function Spinner({ size, show, children, className }: SpinnerContentProps) {
+ return (
+
+
+ {children}
+
+ );
+}
diff --git a/frontend/src/pages/ReviewDetail.tsx b/frontend/src/pages/ReviewDetail.tsx
index b36ae4a..4aafcd9 100644
--- a/frontend/src/pages/ReviewDetail.tsx
+++ b/frontend/src/pages/ReviewDetail.tsx
@@ -21,23 +21,28 @@ export default function ReviewDetail(): JSX.Element {
const { data: reviewDetail } = useReviewDetailQuery(Number(projectId), Number(reviewId), memberId);
- const approveReviewMutation = useApproveReviewQuery({ projectId: Number(projectId), reviewId: Number(reviewId) });
- const rejectReviewMutation = useRejectReviewQuery({ projectId: Number(projectId), reviewId: Number(reviewId) });
+ const approveReviewMutation = useApproveReviewQuery({
+ projectId: Number(projectId),
+ reviewId: Number(reviewId),
+ memberId: memberId,
+ });
+ const rejectReviewMutation = useRejectReviewQuery({
+ projectId: Number(projectId),
+ reviewId: Number(reviewId),
+ memberId: memberId,
+ });
const [activeTab, setActiveTab] = useState<'content' | 'images'>('content');
- const [isReviewed, setIsReviewed] = useState(
- reviewDetail?.reviewStatus === 'APPROVED' || reviewDetail?.reviewStatus === 'REJECTED'
- );
const handleApprove = () => {
approveReviewMutation.mutate(undefined, {
- onSuccess: () => setIsReviewed(true),
+ onSuccess: () => {},
});
};
const handleReject = () => {
rejectReviewMutation.mutate(undefined, {
- onSuccess: () => setIsReviewed(true),
+ onSuccess: () => {},
});
};
@@ -124,24 +129,20 @@ export default function ReviewDetail(): JSX.Element {
)}
- {!isReviewed && (
+ {reviewDetail.reviewStatus !== 'APPROVED' && reviewDetail.reviewStatus !== 'REJECTED' && (
- {reviewDetail.reviewStatus !== 'APPROVED' && (
-
- )}
- {reviewDetail.reviewStatus !== 'REJECTED' && (
-
- )}
+
+
)}
diff --git a/frontend/src/queries/comments/useGetCommentListQuery.ts b/frontend/src/queries/comments/useCommentListQuery.ts
similarity index 74%
rename from frontend/src/queries/comments/useGetCommentListQuery.ts
rename to frontend/src/queries/comments/useCommentListQuery.ts
index 679945a..d8d75da 100644
--- a/frontend/src/queries/comments/useGetCommentListQuery.ts
+++ b/frontend/src/queries/comments/useCommentListQuery.ts
@@ -1,7 +1,7 @@
import { getCommentList } from '@/api/commentAPi';
import { useSuspenseQuery } from '@tanstack/react-query';
-export default function useGetCommentListQuery(projectId: number, imageId: number) {
+export default function useCommentListQuery(projectId: number, imageId: number) {
return useSuspenseQuery({
queryKey: ['commentList', projectId, imageId],
queryFn: () => getCommentList(projectId, imageId),
diff --git a/frontend/src/queries/comments/useGetCommentQuery.ts b/frontend/src/queries/comments/useCommentQuery.ts
similarity index 74%
rename from frontend/src/queries/comments/useGetCommentQuery.ts
rename to frontend/src/queries/comments/useCommentQuery.ts
index c828302..c3b93b9 100644
--- a/frontend/src/queries/comments/useGetCommentQuery.ts
+++ b/frontend/src/queries/comments/useCommentQuery.ts
@@ -1,7 +1,7 @@
import { getComment } from '@/api/commentAPi';
import { useSuspenseQuery } from '@tanstack/react-query';
-export default function useGetCommentQuery(projectId: number, commentId: number) {
+export default function useCommentQuery(projectId: number, commentId: number) {
return useSuspenseQuery({
queryKey: ['comment', projectId, commentId],
queryFn: () => getComment(projectId, commentId),
diff --git a/frontend/src/queries/comments/useCreateCommentMutation.ts b/frontend/src/queries/comments/useCreateCommentQuery.ts
similarity index 83%
rename from frontend/src/queries/comments/useCreateCommentMutation.ts
rename to frontend/src/queries/comments/useCreateCommentQuery.ts
index cd530f6..f0d7153 100644
--- a/frontend/src/queries/comments/useCreateCommentMutation.ts
+++ b/frontend/src/queries/comments/useCreateCommentQuery.ts
@@ -2,7 +2,7 @@ import { createComment } from '@/api/commentAPi';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { CommentResponse } from '@/types';
-export default function useCreateCommentMutation(projectId: number, imageId: number) {
+export default function useCreateCommentQuery(projectId: number, imageId: number) {
const queryClient = useQueryClient();
return useMutation({
diff --git a/frontend/src/queries/comments/useDeleteCommentMutation.ts b/frontend/src/queries/comments/useDeleteCommentQuery.ts
similarity index 67%
rename from frontend/src/queries/comments/useDeleteCommentMutation.ts
rename to frontend/src/queries/comments/useDeleteCommentQuery.ts
index dd60cbc..4634b2d 100644
--- a/frontend/src/queries/comments/useDeleteCommentMutation.ts
+++ b/frontend/src/queries/comments/useDeleteCommentQuery.ts
@@ -1,11 +1,11 @@
import { deleteComment } from '@/api/commentAPi';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-export default function useDeleteCommentMutation(projectId: number, commentId: number) {
+export default function useDeleteCommentQuery(projectId: number) {
const queryClient = useQueryClient();
return useMutation({
- mutationFn: () => deleteComment(projectId, commentId),
+ mutationFn: (commentId: number) => deleteComment(projectId, commentId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['commentList', projectId] });
},
diff --git a/frontend/src/queries/comments/useUpdateCommentMutation.ts b/frontend/src/queries/comments/useUpdateCommentMutation.ts
deleted file mode 100644
index 22bc051..0000000
--- a/frontend/src/queries/comments/useUpdateCommentMutation.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { updateComment } from '@/api/commentAPi';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { CommentResponse } from '@/types';
-
-export default function useUpdateCommentMutation(projectId: number, commentId: number) {
- const queryClient = useQueryClient();
-
- return useMutation({
- mutationFn: (commentData: CommentResponse) => updateComment(projectId, commentId, commentData),
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['comment', projectId, commentId] });
- queryClient.invalidateQueries({ queryKey: ['commentList', projectId] });
- },
- });
-}
diff --git a/frontend/src/queries/comments/useUpdateCommentQuery.ts b/frontend/src/queries/comments/useUpdateCommentQuery.ts
new file mode 100644
index 0000000..185d279
--- /dev/null
+++ b/frontend/src/queries/comments/useUpdateCommentQuery.ts
@@ -0,0 +1,15 @@
+import { updateComment } from '@/api/commentAPi';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { CommentRequest } from '@/types';
+
+export default function useUpdateCommentQuery(projectId: number) {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: ({ commentId, commentData }: { commentId: number; commentData: CommentRequest }) =>
+ updateComment(projectId, commentId, commentData),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['commentList', projectId] });
+ },
+ });
+}
diff --git a/frontend/src/queries/reviews/useApproveReviewQuery.ts b/frontend/src/queries/reviews/useApproveReviewQuery.ts
index ae8a922..c394a9b 100644
--- a/frontend/src/queries/reviews/useApproveReviewQuery.ts
+++ b/frontend/src/queries/reviews/useApproveReviewQuery.ts
@@ -4,15 +4,16 @@ import { approveReview } from '@/api/reviewApi';
interface ReviewStatusChangeProps {
projectId: number;
reviewId: number;
+ memberId: number;
}
-export default function useApproveReviewQuery({ projectId, reviewId }: ReviewStatusChangeProps) {
+export default function useApproveReviewQuery({ projectId, reviewId, memberId }: ReviewStatusChangeProps) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => approveReview(projectId, reviewId),
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['reviewDetail', reviewId] });
+ queryClient.invalidateQueries({ queryKey: ['reviewDetail', projectId, reviewId, memberId] });
},
});
}
diff --git a/frontend/src/queries/reviews/useRejectReviewQuery.ts b/frontend/src/queries/reviews/useRejectReviewQuery.ts
index b3bfd6a..28c7b80 100644
--- a/frontend/src/queries/reviews/useRejectReviewQuery.ts
+++ b/frontend/src/queries/reviews/useRejectReviewQuery.ts
@@ -4,15 +4,16 @@ import { rejectReview } from '@/api/reviewApi';
interface ReviewStatusChangeProps {
projectId: number;
reviewId: number;
+ memberId: number;
}
-export default function useRejectReviewQuery({ projectId, reviewId }: ReviewStatusChangeProps) {
+export default function useRejectReviewQuery({ projectId, reviewId, memberId }: ReviewStatusChangeProps) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => rejectReview(projectId, reviewId),
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['reviewDetail', reviewId] });
+ queryClient.invalidateQueries({ queryKey: ['reviewDetail', projectId, reviewId, memberId] });
},
});
}
diff --git a/frontend/src/stores/useCanvasStore.ts b/frontend/src/stores/useCanvasStore.ts
index b3dde24..46e9e6d 100644
--- a/frontend/src/stores/useCanvasStore.ts
+++ b/frontend/src/stores/useCanvasStore.ts
@@ -5,7 +5,7 @@ interface CanvasState {
sidebarSize: number;
image: ImageResponse | null;
labels: Label[];
- drawState: 'pen' | 'rect' | 'pointer';
+ drawState: 'pen' | 'rect' | 'pointer' | 'comment';
selectedLabelId: number | null;
setSidebarSize: (width: number) => void;
setImage: (image: ImageResponse | null) => void;
@@ -13,7 +13,7 @@ interface CanvasState {
addLabel: (label: Label) => void;
removeLabel: (labelId: number) => void;
updateLabel: (label: Label) => void;
- setDrawState: (state: 'pen' | 'rect' | 'pointer') => void;
+ setDrawState: (state: 'pen' | 'rect' | 'pointer' | 'comment') => void;
setSelectedLabelId: (labelId: number | null) => void;
}
diff --git a/frontend/src/stores/useCommentStore.ts b/frontend/src/stores/useCommentStore.ts
index de7312b..b0d00f3 100644
--- a/frontend/src/stores/useCommentStore.ts
+++ b/frontend/src/stores/useCommentStore.ts
@@ -4,34 +4,33 @@ import { CommentResponse } from '@/types';
interface CommentWithToggle extends CommentResponse {
isOpen?: boolean;
}
+
interface CommentState {
comments: CommentWithToggle[];
- addComment: (comment: CommentResponse) => void;
- updateComment: (updatedComment: CommentResponse) => void;
+ setComments: (comments: CommentWithToggle[]) => void;
+ addComment: (comment: CommentWithToggle) => void;
+ updateComment: (updatedComment: CommentWithToggle) => void;
deleteComment: (commentId: number) => void;
toggleComment: (commentId: number) => void;
}
const useCommentStore = create((set) => ({
comments: [],
-
+ setComments: (comments) => set({ comments }),
addComment: (comment) =>
set((state) => ({
- comments: [...state.comments, { ...comment, isOpen: false }],
+ comments: [...state.comments, { ...comment, isOpen: true }],
})),
-
updateComment: (updatedComment) =>
set((state) => ({
comments: state.comments.map((comment) =>
comment.id === updatedComment.id ? { ...updatedComment, isOpen: comment.isOpen } : comment
),
})),
-
deleteComment: (commentId) =>
set((state) => ({
comments: state.comments.filter((comment) => comment.id !== commentId),
})),
-
toggleComment: (commentId) =>
set((state) => ({
comments: state.comments.map((comment) =>
diff --git a/frontend/src/types/alarmTypes.ts b/frontend/src/types/alarmTypes.ts
new file mode 100644
index 0000000..af82f5e
--- /dev/null
+++ b/frontend/src/types/alarmTypes.ts
@@ -0,0 +1,6 @@
+export interface AlarmResponse {
+ id: number;
+ isRead: boolean;
+ createdAt: string;
+ type: string;
+}
diff --git a/frontend/src/types/categoryTypes.ts b/frontend/src/types/categoryTypes.ts
new file mode 100644
index 0000000..e08f955
--- /dev/null
+++ b/frontend/src/types/categoryTypes.ts
@@ -0,0 +1,9 @@
+// 카테고리 관련 DTO
+export interface CategoryRequest {
+ categoryName: string;
+}
+
+export interface CategoryResponse {
+ id: number;
+ name: string;
+}
diff --git a/frontend/src/types/commentTypes.ts b/frontend/src/types/commentTypes.ts
new file mode 100644
index 0000000..cf2a1c5
--- /dev/null
+++ b/frontend/src/types/commentTypes.ts
@@ -0,0 +1,24 @@
+import { MemberResponse } from './memberTypes';
+
+// 댓글 관련 DTO
+export interface CommentRequest {
+ content: string;
+ positionX: number;
+ positionY: number;
+}
+
+export interface CommentResponse {
+ id: number;
+ memberId: number;
+ memberNickname: string;
+ memberProfileImage: string;
+ positionX: number;
+ positionY: number;
+ content: string;
+ createTime: string; // 작성 일자 (ISO 8601 형식)
+ author: MemberResponse; // 추가됨
+}
+
+export interface CommentListResponse {
+ commentResponses: CommentResponse[];
+}
diff --git a/frontend/src/types/fileTypes.ts b/frontend/src/types/fileTypes.ts
new file mode 100644
index 0000000..7d96a02
--- /dev/null
+++ b/frontend/src/types/fileTypes.ts
@@ -0,0 +1,27 @@
+// 파일 및 디렉터리 관련 타입
+export type FileItem = {
+ id: number;
+ name: string;
+ url: string;
+ type: 'image' | 'json';
+ status: 'idle' | 'done';
+};
+
+export type DirectoryItem = {
+ id: number;
+ name: string;
+ type: 'directory';
+ children: Array;
+};
+
+export type Project = {
+ id: number;
+ name: string;
+ type: 'classification' | 'detection' | 'segmentation';
+ children: Array;
+};
+export type Workspace = {
+ id: number;
+ name: string;
+ projects: Array;
+};
diff --git a/frontend/src/types/folderTypes.ts b/frontend/src/types/folderTypes.ts
new file mode 100644
index 0000000..eb91acc
--- /dev/null
+++ b/frontend/src/types/folderTypes.ts
@@ -0,0 +1,23 @@
+import { ImageResponse } from './imageTypes';
+
+export interface FolderRequest {
+ title: string;
+ parentId: number;
+}
+
+export interface ChildFolder {
+ id: number;
+ title: string;
+}
+
+export interface FolderResponse {
+ id: number;
+ title: string;
+ images: ImageResponse[];
+ children: ChildFolder[];
+}
+
+export interface FolderIdResponse {
+ id: number;
+ title: string;
+}
diff --git a/frontend/src/types/imageTypes.ts b/frontend/src/types/imageTypes.ts
new file mode 100644
index 0000000..4fe094f
--- /dev/null
+++ b/frontend/src/types/imageTypes.ts
@@ -0,0 +1,33 @@
+export type ImageStatus = 'PENDING' | 'IN_PROGRESS' | 'SAVE' | 'REVIEW_REQUEST' | 'REVIEW_REJECTED' | 'COMPLETED';
+
+export interface ImageResponse {
+ id: number;
+ imageTitle: string;
+ imagePath: string;
+ dataPath: string;
+ status: ImageStatus;
+}
+
+// 이미지 이동 및 상태변경 요청 DTO
+export interface ImageMoveRequest {
+ moveFolderId: number;
+}
+
+export interface ImageStatusChangeRequest {
+ labelStatus: ImageStatus;
+}
+
+// 이미지 상세 조회 응답 DTO
+export interface ImageDetailResponse {
+ id: number;
+ imageTitle: string;
+ imageUrl: string;
+ data: string | null;
+ status: ImageStatus;
+}
+export interface ImageFolderRequest {
+ memberId: number;
+ projectId: number;
+ parentId: number;
+ files: File[];
+}
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index c71c479..2cbfc3e 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -1,355 +1,13 @@
-// 파일 및 디렉터리 관련 타입
-export type FileItem = {
- id: number;
- name: string;
- url: string;
- type: 'image' | 'json';
- status: 'idle' | 'done';
-};
-
-export type DirectoryItem = {
- id: number;
- name: string;
- type: 'directory';
- children: Array;
-};
-
-// 프로젝트 관련 타입
-export type Project = {
- id: number;
- name: string;
- type: 'classification' | 'detection' | 'segmentation';
- children: Array;
-};
-
-// 워크스페이스 관련 타입
-export type Workspace = {
- id: number;
- name: string;
- projects: Array;
-};
-
-// 레이블 관련 타입
-export type Label = {
- id: number;
- categoryId: number;
- color: string;
- type: 'polygon' | 'rectangle' | 'point';
- coordinates: Array<[number, number]>;
-};
-
-export interface LabelingRequest {
- memberId: number;
- projectId: number;
- imageId: number;
-}
-
-export interface AutoLabelingResponse {
- imageId: number;
- imageUrl: string;
- data: string;
-}
-
-// 폴더 및 이미지 관련 DTO
-export interface FolderRequest {
- title: string;
- parentId: number;
-}
-
-export interface ChildFolder {
- id: number;
- title: string;
-}
-
-export interface FolderResponse {
- id: number;
- title: string;
- images: ImageResponse[];
- children: ChildFolder[];
-}
-
-export type ImageStatus = 'PENDING' | 'IN_PROGRESS' | 'SAVE' | 'REVIEW_REQUEST' | 'REVIEW_REJECTED' | 'COMPLETED';
-
-export interface ImageResponse {
- id: number;
- imageTitle: string;
- imagePath: string;
- dataPath: string;
- status: ImageStatus;
-}
-
-// 이미지 이동 및 상태변경 요청 DTO
-export interface ImageMoveRequest {
- moveFolderId: number;
-}
-
-export interface ImageStatusChangeRequest {
- labelStatus: ImageStatus;
-}
-
-// 멤버 관련 DTO
-export interface MemberResponse {
- id: number;
- nickname: string;
- profileImage: string;
- email: string;
-}
-
-// 워크스페이스 관련 DTO
-export interface WorkspaceMemberResponse {
- id: number;
- nickname: string;
- profileImage: string;
-}
-export interface WorkspaceRequest {
- title: string;
- content: string;
-}
-
-export interface WorkspaceResponse {
- id: number;
- memberId: string;
- title: string;
- content: string;
- createdAt: string;
- updatedAt: string;
-}
-
-export interface WorkspaceListResponse {
- workspaceResponses: WorkspaceResponse[];
-}
-
-export interface ProjectRequest {
- title: string;
- projectType: 'classification' | 'detection' | 'segmentation';
- categories: string[];
-}
-
-export type ProjectResponse = {
- id: number;
- title: string;
- workspaceId: number;
- projectType: 'classification' | 'detection' | 'segmentation';
- createdAt: string;
- updatedAt: string;
- thumbnail?: string; // Optional
-};
-
-// 댓글 관련 DTO
-export interface CommentRequest {
- content: string;
- positionX: number;
- positionY: number;
-}
-
-export interface CommentResponse {
- id: number;
- memberId: number;
- memberNickname: string;
- memberProfileImage: string;
- positionX: number;
- positionY: number;
- content: string;
- createTime: string; // 작성 일자 (ISO 8601 형식)
- author: MemberResponse; // 추가됨
-}
-
-export interface CommentListResponse {
- commentResponses: CommentResponse[];
-}
-
-// 프로젝트 멤버 관련 DTO
-export interface ProjectMemberRequest {
- memberId: number;
- privilegeType: 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER';
-}
-
-export interface ProjectMemberResponse {
- memberId: number;
- nickname: string;
- profileImage: string;
- privilegeType: 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER';
-}
-
-// 리뷰 관련 DTO
-export interface ReviewRequest {
- title: string;
- content: string;
- imageIds: number[];
-}
-
-export interface ReviewResponse {
- reviewId: number;
- projectId: number;
- title: string;
- content: string;
- status: 'REQUESTED' | 'APPROVED' | 'REJECTED';
- author: MemberResponse;
- createAt: string;
- updateAt: string;
-}
-
-export interface ReviewStatusRequest {
- reviewStatus: 'REQUESTED' | 'APPROVED' | 'REJECTED';
-}
-
-export interface ReviewImageResponse {
- id: number;
- imageTitle: string;
- status: ImageStatus;
- imagePath: string;
- dataPath: string;
-}
-
-export interface ReviewDetailResponse {
- reviewId: number;
- title: string;
- content: string;
- reviewStatus: 'REQUESTED' | 'APPROVED' | 'REJECTED';
- images: ReviewImageResponse[];
- createAt: string;
- updateAt: string;
- author: MemberResponse;
- reviewer: MemberResponse;
-}
-
-// 카테고리 관련 DTO
-export interface CategoryRequest {
- categoryName: string;
-}
-
-export interface CategoryResponse {
- id: number;
- name: string;
-}
-
-// 레이블 저장 요청 DTO
-export interface LabelSaveRequest {
- data: string;
-}
-
-// 폴더 ID 응답 DTO
-export interface FolderIdResponse {
- id: number;
- title: string;
-}
-
-// 이미지 상세 조회 응답 DTO
-export interface ImageDetailResponse {
- id: number;
- imageTitle: string;
- imageUrl: string;
- data: string | null;
- status: ImageStatus;
-}
-
-// 리프레시 토큰 응답 DTO
-export interface RefreshTokenResponse {
- accessToken: string;
-}
-
-export interface Shape {
- categoryId: number;
- color: string;
- points: [number, number][];
- group_id: number;
- shape_type: 'polygon' | 'rectangle' | 'point';
- flags: Record;
-}
-
-export interface LabelJson {
- version: string;
- task_type: 'cls' | 'det' | 'seg';
- shapes: Shape[];
- split: string;
- imageHeight: number;
- imageWidth: number;
- imageDepth: number;
-}
-
-export interface ErrorResponse {
- status: number;
- code: number;
- message: string;
- isSuccess: boolean;
-}
-
-export interface ImageFolderRequest {
- memberId: number;
- projectId: number;
- parentId: number;
- files: File[];
-}
-export interface LabelCategoryResponse {
- id: number;
- labelName: string;
-}
-// 카테고리 요청 DTO
-export interface LabelCategoryRequest {
- labelCategoryList: number[];
-}
-
-// 모델 카테고리 응답 DTO
-export interface ModelCategoryResponse {
- id: number;
- name: string;
-}
-
-// 모델 요청 DTO (API로 전달할 데이터 타입)
-export interface ModelRequest {
- name: string;
-}
-
-// 모델 응답 DTO (API로부터 받는 데이터 타입)
-export interface ModelResponse {
- id: number;
- name: string;
- isDefault: boolean;
- isTrain: boolean;
-}
-
-// 프로젝트 모델 리스트 응답 DTO
-export interface ProjectModelsResponse extends Array {}
-// 모델 훈련 요청 DTO
-export interface ModelTrainRequest {
- modelId: number;
- ratio: number;
- epochs: number;
- batch: number;
- lr0: number;
- lrf: number;
- optimizer: 'AUTO' | 'SGD' | 'ADAM' | 'ADAMW' | 'NADAM' | 'RADAM' | 'RMSPROP';
-}
-export interface ResultResponse {
- id: number;
- precision: number;
- recall: number;
- fitness: number;
- ratio: number;
- epochs: number;
- batch: number;
- lr0: number;
- lrf: number;
- optimizer: 'AUTO' | 'SGD' | 'ADAM' | 'ADAMW' | 'NADAM' | 'RADAM' | 'RMSPROP';
- map50: number;
- map5095: number;
-}
-
-export interface ReportResponse {
- modelId: number;
- totalEpochs: number;
- epoch: number;
- boxLoss: number;
- clsLoss: number;
- dflLoss: number;
- fitness: number;
- epochTime: number;
- leftSecond: number;
-}
-
-export interface AlarmResponse {
- id: number;
- isRead: boolean;
- createdAt: string;
- type: string;
-}
+export * from './modelTypes';
+export * from './imageTypes';
+export * from './reviewTypes';
+export * from './alarmTypes';
+export * from './memberTypes';
+export * from './categoryTypes';
+export * from './workspaceTypes';
+export * from './commentTypes';
+export * from './folderTypes';
+export * from './projectTypes';
+export * from './labelTypes';
+export * from './fileTypes';
+export * from './reportTypes';
diff --git a/frontend/src/types/labelTypes.ts b/frontend/src/types/labelTypes.ts
new file mode 100644
index 0000000..9ff1eea
--- /dev/null
+++ b/frontend/src/types/labelTypes.ts
@@ -0,0 +1,53 @@
+// 레이블 관련 타입
+export type Label = {
+ id: number;
+ categoryId: number;
+ color: string;
+ type: 'polygon' | 'rectangle' | 'point';
+ coordinates: Array<[number, number]>;
+};
+
+export interface LabelingRequest {
+ memberId: number;
+ projectId: number;
+ imageId: number;
+}
+
+export interface AutoLabelingResponse {
+ imageId: number;
+ imageUrl: string;
+ data: string;
+}
+
+// 레이블 저장 요청 DTO
+export interface LabelSaveRequest {
+ data: string;
+}
+
+export interface Shape {
+ categoryId: number;
+ color: string;
+ points: [number, number][];
+ group_id: number;
+ shape_type: 'polygon' | 'rectangle' | 'point';
+ flags: Record;
+}
+
+export interface LabelJson {
+ version: string;
+ task_type: 'cls' | 'det' | 'seg';
+ shapes: Shape[];
+ split: string;
+ imageHeight: number;
+ imageWidth: number;
+ imageDepth: number;
+}
+
+export interface LabelCategoryResponse {
+ id: number;
+ labelName: string;
+}
+// 카테고리 요청 DTO
+export interface LabelCategoryRequest {
+ labelCategoryList: number[];
+}
diff --git a/frontend/src/types/memberTypes.ts b/frontend/src/types/memberTypes.ts
new file mode 100644
index 0000000..53688b8
--- /dev/null
+++ b/frontend/src/types/memberTypes.ts
@@ -0,0 +1,11 @@
+// 멤버 관련 DTO
+export interface MemberResponse {
+ id: number;
+ nickname: string;
+ profileImage: string;
+ email: string;
+}
+// 리프레시 토큰 응답 DTO
+export interface RefreshTokenResponse {
+ accessToken: string;
+}
diff --git a/frontend/src/types/modelTypes.ts b/frontend/src/types/modelTypes.ts
new file mode 100644
index 0000000..16ac74f
--- /dev/null
+++ b/frontend/src/types/modelTypes.ts
@@ -0,0 +1,32 @@
+// 모델 카테고리 응답 DTO
+export interface ModelCategoryResponse {
+ id: number;
+ name: string;
+}
+
+// 모델 요청 DTO (API로 전달할 데이터 타입)
+export interface ModelRequest {
+ name: string;
+}
+
+// 모델 응답 DTO (API로부터 받는 데이터 타입)
+export interface ModelResponse {
+ id: number;
+ name: string;
+ isDefault: boolean;
+ isTrain: boolean;
+ projectType: 'classification' | 'detection' | 'segmentation';
+}
+
+// 프로젝트 모델 리스트 응답 DTO
+export interface ProjectModelsResponse extends Array {}
+// 모델 훈련 요청 DTO
+export interface ModelTrainRequest {
+ modelId: number;
+ ratio: number;
+ epochs: number;
+ batch: number;
+ lr0: number;
+ lrf: number;
+ optimizer: 'AUTO' | 'SGD' | 'ADAM' | 'ADAMW' | 'NADAM' | 'RADAM' | 'RMSPROP';
+}
diff --git a/frontend/src/types/projectTypes.ts b/frontend/src/types/projectTypes.ts
new file mode 100644
index 0000000..25af50b
--- /dev/null
+++ b/frontend/src/types/projectTypes.ts
@@ -0,0 +1,27 @@
+export interface ProjectRequest {
+ title: string;
+ projectType: 'classification' | 'detection' | 'segmentation';
+ categories: string[];
+}
+
+export type ProjectResponse = {
+ id: number;
+ title: string;
+ workspaceId: number;
+ projectType: 'classification' | 'detection' | 'segmentation';
+ createdAt: string;
+ updatedAt: string;
+ thumbnail?: string; // Optional
+};
+// 프로젝트 멤버 관련 DTO
+export interface ProjectMemberRequest {
+ memberId: number;
+ privilegeType: 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER';
+}
+
+export interface ProjectMemberResponse {
+ memberId: number;
+ nickname: string;
+ profileImage: string;
+ privilegeType: 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER';
+}
diff --git a/frontend/src/types/reportTypes.ts b/frontend/src/types/reportTypes.ts
new file mode 100644
index 0000000..246bb1b
--- /dev/null
+++ b/frontend/src/types/reportTypes.ts
@@ -0,0 +1,27 @@
+export interface ResultResponse {
+ id: number;
+ precision: number;
+ recall: number;
+ fitness: number;
+ ratio: number;
+ epochs: number;
+ batch: number;
+ lr0: number;
+ lrf: number;
+ optimizer: 'AUTO' | 'SGD' | 'ADAM' | 'ADAMW' | 'NADAM' | 'RADAM' | 'RMSPROP';
+ map50: number;
+ map5095: number;
+}
+
+export interface ReportResponse {
+ modelId: number;
+ totalEpochs: number;
+ epoch: number;
+ boxLoss: number;
+ clsLoss: number;
+ dflLoss: number;
+ fitness: number;
+ epochTime: number;
+ leftSecond: number;
+ segLoss: number;
+}
diff --git a/frontend/src/types/reviewTypes.ts b/frontend/src/types/reviewTypes.ts
new file mode 100644
index 0000000..ec7d65a
--- /dev/null
+++ b/frontend/src/types/reviewTypes.ts
@@ -0,0 +1,44 @@
+import { ImageStatus } from './imageTypes';
+import { MemberResponse } from './memberTypes';
+
+// 리뷰 관련 DTO
+export interface ReviewRequest {
+ title: string;
+ content: string;
+ imageIds: number[];
+}
+
+export interface ReviewResponse {
+ reviewId: number;
+ projectId: number;
+ title: string;
+ content: string;
+ status: 'REQUESTED' | 'APPROVED' | 'REJECTED';
+ author: MemberResponse;
+ createAt: string;
+ updateAt: string;
+}
+
+export interface ReviewStatusRequest {
+ reviewStatus: 'REQUESTED' | 'APPROVED' | 'REJECTED';
+}
+
+export interface ReviewImageResponse {
+ id: number;
+ imageTitle: string;
+ status: ImageStatus;
+ imagePath: string;
+ dataPath: string;
+}
+
+export interface ReviewDetailResponse {
+ reviewId: number;
+ title: string;
+ content: string;
+ reviewStatus: 'REQUESTED' | 'APPROVED' | 'REJECTED';
+ images: ReviewImageResponse[];
+ createAt: string;
+ updateAt: string;
+ author: MemberResponse;
+ reviewer: MemberResponse;
+}
diff --git a/frontend/src/types/workspaceTypes.ts b/frontend/src/types/workspaceTypes.ts
new file mode 100644
index 0000000..09f89fa
--- /dev/null
+++ b/frontend/src/types/workspaceTypes.ts
@@ -0,0 +1,23 @@
+// 워크스페이스 관련 DTO
+export interface WorkspaceMemberResponse {
+ id: number;
+ nickname: string;
+ profileImage: string;
+}
+export interface WorkspaceRequest {
+ title: string;
+ content: string;
+}
+
+export interface WorkspaceResponse {
+ id: number;
+ memberId: string;
+ title: string;
+ content: string;
+ createdAt: string;
+ updatedAt: string;
+}
+
+export interface WorkspaceListResponse {
+ workspaceResponses: WorkspaceResponse[];
+}