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 @@ + + + + + delete [#1487] + Created with Sketch. + + + + + + + + + + + + + \ 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[]; +}