From 095e29ad7afd057716548a21fcba6659b07e971e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Fri, 4 Oct 2024 14:26:39 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Fix:=20=EB=A9=A4=EB=B2=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=90=98=EA=B2=8C=20api=20=EC=9E=AC=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/projectApi.ts | 13 +++++++------ .../useUpdateProjectMemberPrivilegeQuery.ts | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/frontend/src/api/projectApi.ts b/frontend/src/api/projectApi.ts index 526b4e5..f0eefde 100644 --- a/frontend/src/api/projectApi.ts +++ b/frontend/src/api/projectApi.ts @@ -80,13 +80,14 @@ export async function addProjectMember(projectId: number, memberId: number, newM export async function updateProjectMemberPrivilege( projectId: number, memberId: number, - privilegeData: ProjectMemberRequest + privilegeType: ProjectMemberResponse['privilegeType'] // 수정 가능한 권한 타입으로 변경 ) { - return api - .put(`/projects/${projectId}/members`, privilegeData, { - params: { memberId }, - }) - .then(({ data }) => data); + const privilegeData = { + memberId, + privilegeType, + }; + + return api.put(`/projects/${projectId}/members`, privilegeData).then(({ data }) => data); } // 프로젝트 멤버 삭제 diff --git a/frontend/src/queries/projects/useUpdateProjectMemberPrivilegeQuery.ts b/frontend/src/queries/projects/useUpdateProjectMemberPrivilegeQuery.ts index 4f60f1d..69aaf6b 100644 --- a/frontend/src/queries/projects/useUpdateProjectMemberPrivilegeQuery.ts +++ b/frontend/src/queries/projects/useUpdateProjectMemberPrivilegeQuery.ts @@ -1,6 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { updateProjectMemberPrivilege } from '@/api/projectApi'; -import { ProjectMemberRequest } from '@/types'; +import { ProjectMemberResponse } from '@/types'; export default function useUpdateProjectMemberPrivilegeQuery() { const queryClient = useQueryClient(); @@ -9,12 +9,12 @@ export default function useUpdateProjectMemberPrivilegeQuery() { mutationFn: ({ projectId, memberId, - privilegeData, + privilegeType, }: { projectId: number; memberId: number; - privilegeData: ProjectMemberRequest; - }) => updateProjectMemberPrivilege(projectId, memberId, privilegeData), + privilegeType: ProjectMemberResponse['privilegeType']; + }) => updateProjectMemberPrivilege(projectId, memberId, privilegeType), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['projectMembers', variables.projectId] }); }, From f14e0f27c54beead6e190a33c89d52d68b92a57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Fri, 4 Oct 2024 14:34:41 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Refactor:=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImageUploadPresignedModal/index.tsx | 4 +- .../ProjectCreateModal/ProjectCreateForm.tsx | 30 +++++++-- .../WorkspaceBrowseLayout/index.tsx | 1 - .../WorkspaceDropdownMenu/index.tsx | 67 ++++++++++--------- 4 files changed, 62 insertions(+), 40 deletions(-) diff --git a/frontend/src/components/ImageUploadPresignedModal/index.tsx b/frontend/src/components/ImageUploadPresignedModal/index.tsx index bf134e3..11a641d 100644 --- a/frontend/src/components/ImageUploadPresignedModal/index.tsx +++ b/frontend/src/components/ImageUploadPresignedModal/index.tsx @@ -6,7 +6,7 @@ import ImageUploadPresingedForm from '@/components/ImageUploadPresignedModal/Ima export default function ImageUploadPresignedModal({ projectId, folderId }: { projectId: number; folderId: number }) { const [isOpen, setIsOpen] = React.useState(false); const [fileCount, setFileCount] = React.useState(0); - + const handleOpen = () => setIsOpen(true); const handleClose = () => setIsOpen(false); const handleFileCount = (fileCount: number) => { @@ -27,7 +27,7 @@ export default function ImageUploadPresignedModal({ projectId, folderId }: { pro - 0 ? `파일 업로드 (${fileCount}) PreSigned` : '파일 업로드 PreSigned'} /> + 0 ? `파일 업로드 PreSigned (${fileCount})` : '파일 업로드 PreSigned'} />
카테고리
- - 직접 입력 - 파일로 입력 + + + 직접 입력 + + + 파일로 입력 +
@@ -192,7 +202,7 @@ export default function ProjectCreateForm({ onSubmit }: { onSubmit: (data: Proje
handleDrop(event, field.onChange)} onClick={() => fileInputRef.current?.click()} > -

- 파일을 업로드하려면 여기를 클릭하거나 파일을 드래그하여 여기에 놓으세요. -

+ {isDragging ? ( +

드래그한 파일을 여기에 놓으세요

+ ) : ( +

+ 파일을 업로드하려면 여기를 클릭하거나 +
+ 파일을 드래그하여 여기에 놓으세요. +

+ )} diff --git a/frontend/src/components/WorkspaceDropdownMenu/index.tsx b/frontend/src/components/WorkspaceDropdownMenu/index.tsx index d2c271d..999d22f 100644 --- a/frontend/src/components/WorkspaceDropdownMenu/index.tsx +++ b/frontend/src/components/WorkspaceDropdownMenu/index.tsx @@ -12,7 +12,7 @@ import ImageUploadFileForm from '../ImageUploadFileModal/ImageUploadFileForm'; import ImageUploadFolderFileForm from '../ImageUploadFolderFileModal/ImageUploadFolderFileForm'; import ImageUploadFolderForm from '../ImageUploadFolderModal/ImageUploadFolderForm'; import ImageUploadZipForm from '../ImageUploadZipModal/ImageUploadZipForm'; -import ImageUploadPresignedForm from '../ImageUploadPresignedModal/ImageUploadPresignedForm.tsx' +import ImageUploadPresignedForm from '../ImageUploadPresignedModal/ImageUploadPresignedForm.tsx'; export default function WorkspaceDropdownMenu({ projectId, @@ -25,9 +25,10 @@ export default function WorkspaceDropdownMenu({ }) { const [isOpenUploadFile, setIsOpenUploadFile] = React.useState(false); const [fileCount, setFileCount] = React.useState(0); + const [isOpenUploadPresigned, setIsOpenUploadPresigned] = React.useState(false); + const [presignedCount, setPresignedCount] = React.useState(0); const [isOpenUploadFolderFile, setIsOpenUploadFolderFile] = React.useState(false); const [isOpenUploadFolder, setIsOpenUploadFolder] = React.useState(false); - const [isOpenUploadPresigned, setIsOpenUploadPresigned] = React.useState(false); const [isOpenUploadZip, setIsOpenUploadZip] = React.useState(false); const handleOpenUploadFile = () => setIsOpenUploadFile(true); @@ -36,6 +37,20 @@ export default function WorkspaceDropdownMenu({ setIsOpenUploadFile(false); }; + const handleFileCount = (fileCount: number) => { + setFileCount(fileCount); + }; + + const handleOpenUploadPresigned = () => setIsOpenUploadPresigned(true); + + const handleCloseUploadPresigned = () => { + setIsOpenUploadPresigned(false); + }; + + const handlePresignedCount = (fileCount: number) => { + setPresignedCount(fileCount); + }; + const handleOpenUploadFolderFile = () => setIsOpenUploadFolderFile(true); const handleCloseUploadFolderFile = () => { @@ -48,22 +63,12 @@ export default function WorkspaceDropdownMenu({ setIsOpenUploadFolder(false); }; - const handleOpenUploadPresigned = () => setIsOpenUploadPresigned(true); - - const handleCloseUploadPresigned = () => { - setIsOpenUploadPresigned(false); - }; - const handleOpenUploadZip = () => setIsOpenUploadZip(true); const handleCloseUploadZip = () => { setIsOpenUploadZip(false); }; - const handleFileCount = (fileCount: number) => { - setFileCount(fileCount); - }; - return ( <> @@ -83,9 +88,9 @@ export default function WorkspaceDropdownMenu({ 파일 업로드 + 파일 업로드 (PresignedUrl 이용) 폴더 업로드 (파일 업로드 API 이용) 폴더 업로드 (백엔드 구현 필요) - 폴더 업로드 (PresignedUrl 이용) 폴더 압축파일 업로드 @@ -107,6 +112,25 @@ export default function WorkspaceDropdownMenu({ + + + + 0 ? `파일 업로드 PreSigned (${presignedCount})` : '파일 업로드 PreSigned'} + /> + + + + - - - - - - - - Date: Fri, 4 Oct 2024 15:29:21 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Fix:=20=EC=82=AC=EA=B0=81=ED=98=95=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EC=8B=9C=20=EC=A0=90=20=EC=84=9C=EC=88=9C?= =?UTF-8?q?=20=EB=A7=9E=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-=20S11P2?= =?UTF-8?q?1S002-266?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/lablingApi.ts | 2 +- .../src/components/ImageCanvas/LabelRect.tsx | 4 +- frontend/src/components/ImageCanvas/index.tsx | 7 +-- frontend/src/utils/rectSort.test.ts | 48 +++++++++++++++++++ frontend/src/utils/rectSort.ts | 9 ++++ 5 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 frontend/src/utils/rectSort.test.ts create mode 100644 frontend/src/utils/rectSort.ts diff --git a/frontend/src/api/lablingApi.ts b/frontend/src/api/lablingApi.ts index 0c922c9..efd91a9 100644 --- a/frontend/src/api/lablingApi.ts +++ b/frontend/src/api/lablingApi.ts @@ -11,5 +11,5 @@ export async function saveImageLabels( } export async function runAutoLabel(projectId: number, modelId = 1) { - return api.post(`/projects/${projectId}/auto`, { modelId }).then(({ data }) => data); + return api.post(`/projects/${projectId}/auto`, { modelId }, { timeout: 0 }).then(({ data }) => data); } diff --git a/frontend/src/components/ImageCanvas/LabelRect.tsx b/frontend/src/components/ImageCanvas/LabelRect.tsx index a6cf656..f0b3402 100644 --- a/frontend/src/components/ImageCanvas/LabelRect.tsx +++ b/frontend/src/components/ImageCanvas/LabelRect.tsx @@ -1,4 +1,5 @@ import { Label } from '@/types'; +import rectSort from '@/utils/rectSort'; import Konva from 'konva'; import { useEffect, useRef } from 'react'; import { Line, Transformer } from 'react-konva'; @@ -39,8 +40,9 @@ export default function LabelRect({ [info.coordinates[0][0] * scale.x + rect.x, info.coordinates[0][1] * scale.y + rect.y], [info.coordinates[1][0] * scale.x + rect.x, info.coordinates[1][1] * scale.y + rect.y], ]; + const sortedPoints = rectSort(points as [[number, number], [number, number]]); - setLabel(points); + setLabel(sortedPoints); rectRef.current?.setPosition({ x: 0, y: 0 }); rectRef.current?.scale({ x: 1, y: 1 }); }; diff --git a/frontend/src/components/ImageCanvas/index.tsx b/frontend/src/components/ImageCanvas/index.tsx index 9c4783a..568a8f1 100644 --- a/frontend/src/components/ImageCanvas/index.tsx +++ b/frontend/src/components/ImageCanvas/index.tsx @@ -19,6 +19,7 @@ import { useQueryClient } from '@tanstack/react-query'; import useSaveImageLabelsQuery from '@/queries/projects/useSaveImageLabelsQuery'; import { useToast } from '@/hooks/use-toast'; import CommentLabel from './CommentLabel'; +import rectSort from '@/utils/rectSort'; export default function ImageCanvas() { const { project, folderId, categories } = useProjectStore(); @@ -188,11 +189,11 @@ export default function ImageCanvas() { const endDrawRect = () => { if (drawState !== 'rect' || rectPoints.length === 0) return; + setRectPoints([]); if (rectPoints[0][0] === rectPoints[1][0] && rectPoints[0][1] === rectPoints[1][1]) { - setRectPoints([]); return; } - setRectPoints([]); + const sortedPoints = rectSort(rectPoints as [[number, number], [number, number]]); const color = Math.floor(Math.random() * 0xffffff) .toString(16) @@ -203,7 +204,7 @@ export default function ImageCanvas() { categoryId: categories[0]!.id, type: 'rectangle', color: `#${color}`, - coordinates: rectPoints, + coordinates: sortedPoints, }); setDrawState('pointer'); setSelectedLabelId(id); diff --git a/frontend/src/utils/rectSort.test.ts b/frontend/src/utils/rectSort.test.ts new file mode 100644 index 0000000..4088941 --- /dev/null +++ b/frontend/src/utils/rectSort.test.ts @@ -0,0 +1,48 @@ +import { describe, it, expect } from 'vitest'; +import rectSort from './rectSort'; + +describe('rectSort', () => { + it('should sort coordinates when first point is top-left and second point is bottom-right', () => { + const result = rectSort([ + [1, 2], + [3, 4], + ]); + expect(result).toEqual([ + [1, 2], + [3, 4], + ]); + }); + + it('should sort coordinates when first point is bottom-right and second point is top-left', () => { + const result = rectSort([ + [3, 4], + [1, 2], + ]); + expect(result).toEqual([ + [1, 2], + [3, 4], + ]); + }); + + it('should sort coordinates when first point is bottom-left and second point is top-right', () => { + const result = rectSort([ + [1, 4], + [3, 2], + ]); + expect(result).toEqual([ + [1, 2], + [3, 4], + ]); + }); + + it('should sort coordinates when first point is top-right and second point is bottom-left', () => { + const result = rectSort([ + [3, 2], + [1, 4], + ]); + expect(result).toEqual([ + [1, 2], + [3, 4], + ]); + }); +}); diff --git a/frontend/src/utils/rectSort.ts b/frontend/src/utils/rectSort.ts new file mode 100644 index 0000000..ba43c99 --- /dev/null +++ b/frontend/src/utils/rectSort.ts @@ -0,0 +1,9 @@ +export default function rectSort([[x1, y1], [x2, y2]]: [[number, number], [number, number]]): [ + [number, number], + [number, number], +] { + return [ + [Math.min(x1, x2), Math.min(y1, y2)], + [Math.max(x1, x2), Math.max(y1, y2)], + ]; +}