Merge branch 'fe/develop' of https://lab.ssafy.com/s11-s-project/S11P21S002 into fe/refactor/admin-page
This commit is contained in:
commit
05f8a760ed
@ -42,7 +42,7 @@ export async function changeImageStatus(
|
|||||||
export async function uploadImageList(projectId: number, folderId: number, memberId: number, imageList: File[]) {
|
export async function uploadImageList(projectId: number, folderId: number, memberId: number, imageList: File[]) {
|
||||||
return api
|
return api
|
||||||
.post(
|
.post(
|
||||||
`/projects/${projectId}/folders/${folderId}/images`,
|
`/projects/${projectId}/folders/${folderId}/images/file`,
|
||||||
{ imageList },
|
{ imageList },
|
||||||
{
|
{
|
||||||
params: { memberId },
|
params: { memberId },
|
||||||
@ -51,43 +51,26 @@ export async function uploadImageList(projectId: number, folderId: number, membe
|
|||||||
.then(({ data }) => data);
|
.then(({ data }) => data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadImageFolder(memberId: number, projectId: number, files: File[], parentId: number = 0) {
|
export async function uploadImageFolder(memberId: number, projectId: number, files: File[]) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
formData.append('files', file);
|
formData.append('folderZip', file);
|
||||||
});
|
});
|
||||||
|
|
||||||
return api
|
return api
|
||||||
.post(
|
.post(`/projects/${projectId}/folders/${0}/images/zip`, formData, {
|
||||||
`/projects/${projectId}/folders/${0}/images/upload`,
|
|
||||||
{ folderZip: files, parentId },
|
|
||||||
{
|
|
||||||
params: { memberId },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(({ data }) => data)
|
|
||||||
.catch((error) => {
|
|
||||||
return Promise.reject(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function uploadImageFolderZip(memberId: number, projectId: number, file: File, parentId: number = 0) {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('folderZip', file);
|
|
||||||
formData.append('parentId', parentId.toString());
|
|
||||||
|
|
||||||
// const jsonData = {
|
|
||||||
// parentId,
|
|
||||||
// };
|
|
||||||
// const blob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });
|
|
||||||
// formData.append('parentId', blob);
|
|
||||||
|
|
||||||
return api
|
|
||||||
.post(`/projects/${projectId}/folders/${0}/images/upload`, formData, {
|
|
||||||
params: { memberId },
|
params: { memberId },
|
||||||
})
|
})
|
||||||
.then(({ data }) => data)
|
.then(({ data }) => data);
|
||||||
.catch((error) => {
|
}
|
||||||
return Promise.reject(error);
|
|
||||||
});
|
export async function uploadImageZip(memberId: number, projectId: number, file: File) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('folderZip', file);
|
||||||
|
|
||||||
|
return api
|
||||||
|
.post(`/projects/${projectId}/folders/${0}/images/zip`, formData, {
|
||||||
|
params: { memberId },
|
||||||
|
})
|
||||||
|
.then(({ data }) => data);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import api from '@/api/axiosConfig';
|
import api from '@/api/axiosConfig';
|
||||||
import { LabelingRequest } from '@/types';
|
|
||||||
|
|
||||||
export async function saveImageLabels(projectId: number, imageId: number, memberId: number, data: LabelingRequest) {
|
export async function saveImageLabels(
|
||||||
return api
|
projectId: number,
|
||||||
.post(`/projects/${projectId}/label/image/${imageId}`, data, {
|
imageId: number,
|
||||||
params: { memberId },
|
data: {
|
||||||
})
|
data: string;
|
||||||
.then(({ data }) => data);
|
}
|
||||||
|
) {
|
||||||
|
return api.post(`/projects/${projectId}/label/image/${imageId}`, data).then(({ data }) => data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runAutoLabel(projectId: number, memberId: number) {
|
export async function runAutoLabel(projectId: number, memberId: number) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import useCanvasStore from '@/stores/useCanvasStore';
|
import useCanvasStore from '@/stores/useCanvasStore';
|
||||||
import { LucideIcon, MousePointer2, PenTool, Square } from 'lucide-react';
|
import { LucideIcon, MousePointer2, PenTool, Save, Square } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export default function CanvasControlBar() {
|
export default function CanvasControlBar({ saveJson }: { saveJson: () => void }) {
|
||||||
const drawState = useCanvasStore((state) => state.drawState);
|
const drawState = useCanvasStore((state) => state.drawState);
|
||||||
const setDrawState = useCanvasStore((state) => state.setDrawState);
|
const setDrawState = useCanvasStore((state) => state.setDrawState);
|
||||||
const buttonBaseClassName = 'rounded-lg p-2 transition-colors ';
|
const buttonBaseClassName = 'rounded-lg p-2 transition-colors ';
|
||||||
@ -31,6 +31,16 @@ export default function CanvasControlBar() {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={cn(buttonClassName, buttonBaseClassName)}
|
||||||
|
onClick={saveJson}
|
||||||
|
>
|
||||||
|
<Save
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,13 @@ export default function LabelRect({
|
|||||||
isSelected,
|
isSelected,
|
||||||
onSelect,
|
onSelect,
|
||||||
info,
|
info,
|
||||||
|
setLabel,
|
||||||
dragLayer,
|
dragLayer,
|
||||||
}: {
|
}: {
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
onSelect: (evt: Konva.KonvaEventObject<TouchEvent | MouseEvent>) => void;
|
onSelect: (evt: Konva.KonvaEventObject<TouchEvent | MouseEvent>) => void;
|
||||||
info: Label;
|
info: Label;
|
||||||
|
setLabel: (coordinate: [number, number][]) => void;
|
||||||
dragLayer: Konva.Layer;
|
dragLayer: Konva.Layer;
|
||||||
}) {
|
}) {
|
||||||
const rectRef = useRef<Konva.Line>(null);
|
const rectRef = useRef<Konva.Line>(null);
|
||||||
@ -28,13 +30,19 @@ export default function LabelRect({
|
|||||||
trRef.current?.moveToTop();
|
trRef.current?.moveToTop();
|
||||||
};
|
};
|
||||||
const handleMoveEnd = () => {
|
const handleMoveEnd = () => {
|
||||||
const rectPoints = rectRef.current?.points();
|
const rect = rectRef.current?.getPosition();
|
||||||
const points = [
|
const scale = rectRef.current?.scale();
|
||||||
[rectPoints![0], rectPoints![1]],
|
|
||||||
[rectPoints![4], rectPoints![5]],
|
if (!rect || !scale) return;
|
||||||
|
|
||||||
|
const points: [number, number][] = [
|
||||||
|
[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],
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log(points);
|
setLabel(points);
|
||||||
|
rectRef.current?.setAbsolutePosition({ x: 0, y: 0 });
|
||||||
|
rectRef.current?.scale({ x: 1, y: 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { Circle, Group, Line } from 'react-konva';
|
import { Circle, Group, Line } from 'react-konva';
|
||||||
|
|
||||||
@ -24,6 +25,24 @@ const TRANSFORM_CHANGE_STR = [
|
|||||||
|
|
||||||
export default function PolygonTransformer({ coordinates, setCoordinates, stage, dragLayer }: PolygonTransformerProps) {
|
export default function PolygonTransformer({ coordinates, setCoordinates, stage, dragLayer }: PolygonTransformerProps) {
|
||||||
const anchorsRef = useRef<Konva.Group>(null);
|
const anchorsRef = useRef<Konva.Group>(null);
|
||||||
|
const scale: Vector2d = { x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y };
|
||||||
|
const handleClick = (index: number) => (e: Konva.KonvaEventObject<MouseEvent>) => {
|
||||||
|
if (e.evt.button === 0 && e.evt.detail === 2) {
|
||||||
|
const pos = stage.getRelativePointerPosition()!;
|
||||||
|
const newCoordinates: [number, number][] = [
|
||||||
|
...coordinates.slice(0, index + 1),
|
||||||
|
[pos.x, pos.y],
|
||||||
|
...coordinates.slice(index + 1),
|
||||||
|
];
|
||||||
|
setCoordinates(newCoordinates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.evt.button !== 2) return;
|
||||||
|
|
||||||
|
const newCoordinates = [...coordinates.slice(0, index), ...coordinates.slice(index + 1)];
|
||||||
|
setCoordinates(newCoordinates);
|
||||||
|
};
|
||||||
const handleDragMove = (index: number) => (e: Konva.KonvaEventObject<DragEvent>) => {
|
const handleDragMove = (index: number) => (e: Konva.KonvaEventObject<DragEvent>) => {
|
||||||
const circle = e.target as Konva.Circle;
|
const circle = e.target as Konva.Circle;
|
||||||
const pos = circle.position();
|
const pos = circle.position();
|
||||||
@ -31,7 +50,7 @@ export default function PolygonTransformer({ coordinates, setCoordinates, stage,
|
|||||||
|
|
||||||
newCoordinates[index] = [pos.x, pos.y];
|
newCoordinates[index] = [pos.x, pos.y];
|
||||||
setCoordinates(newCoordinates);
|
setCoordinates(newCoordinates);
|
||||||
stage.batchDraw();
|
// stage.batchDraw();
|
||||||
};
|
};
|
||||||
const handleMouseOver = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
const handleMouseOver = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
||||||
const circle = e.target as Konva.Circle;
|
const circle = e.target as Konva.Circle;
|
||||||
@ -94,10 +113,11 @@ export default function PolygonTransformer({ coordinates, setCoordinates, stage,
|
|||||||
fill="white"
|
fill="white"
|
||||||
draggable
|
draggable
|
||||||
strokeScaleEnabled={false}
|
strokeScaleEnabled={false}
|
||||||
|
onClick={handleClick(index)}
|
||||||
onDragMove={handleDragMove(index)}
|
onDragMove={handleDragMove(index)}
|
||||||
onMouseOver={handleMouseOver}
|
onMouseOver={handleMouseOver}
|
||||||
onMouseOut={handleMouseOut}
|
onMouseOut={handleMouseOut}
|
||||||
scale={{ x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y }}
|
scale={scale}
|
||||||
perfectDrawEnabled={false}
|
perfectDrawEnabled={false}
|
||||||
shadowForStrokeEnabled={false}
|
shadowForStrokeEnabled={false}
|
||||||
/>
|
/>
|
||||||
|
@ -9,9 +9,11 @@ import LabelPolygon from './LabelPolygon';
|
|||||||
import CanvasControlBar from '../CanvasControlBar';
|
import CanvasControlBar from '../CanvasControlBar';
|
||||||
import useLabelJsonQuery from '@/queries/labelJson/useLabelJsonQuery';
|
import useLabelJsonQuery from '@/queries/labelJson/useLabelJsonQuery';
|
||||||
import { Label } from '@/types';
|
import { Label } from '@/types';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
export default function ImageCanvas() {
|
export default function ImageCanvas() {
|
||||||
const { imagePath, dataPath } = useCanvasStore((state) => state.image)!;
|
const { projectId } = useParams<{ projectId: string }>();
|
||||||
|
const { id: imageId, imagePath, dataPath } = useCanvasStore((state) => state.image)!;
|
||||||
const { data: labelData } = useLabelJsonQuery(dataPath);
|
const { data: labelData } = useLabelJsonQuery(dataPath);
|
||||||
const { shapes } = labelData;
|
const { shapes } = labelData;
|
||||||
const selectedLabelId = useCanvasStore((state) => state.selectedLabelId);
|
const selectedLabelId = useCanvasStore((state) => state.selectedLabelId);
|
||||||
@ -43,6 +45,25 @@ export default function ImageCanvas() {
|
|||||||
);
|
);
|
||||||
}, [setLabels, shapes]);
|
}, [setLabels, shapes]);
|
||||||
|
|
||||||
|
const setLabel = (index: number) => (coordinates: [number, number][]) => {
|
||||||
|
const newLabels = [...labels];
|
||||||
|
newLabels[index].coordinates = coordinates;
|
||||||
|
setLabels(newLabels);
|
||||||
|
};
|
||||||
|
const saveJson = () => {
|
||||||
|
const json = JSON.stringify({
|
||||||
|
...labelData,
|
||||||
|
shapes: labels.map(({ name, color, coordinates, type }) => ({
|
||||||
|
label: name,
|
||||||
|
color,
|
||||||
|
shape_type: type === 'polygon' ? 'polygon' : 'rectangle',
|
||||||
|
points: coordinates,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(projectId, imageId, json);
|
||||||
|
// TOOD: api 연결
|
||||||
|
};
|
||||||
const startDrawRect = () => {
|
const startDrawRect = () => {
|
||||||
const { x, y } = stageRef.current!.getRelativePointerPosition()!;
|
const { x, y } = stageRef.current!.getRelativePointerPosition()!;
|
||||||
setRectPoints([
|
setRectPoints([
|
||||||
@ -250,6 +271,7 @@ export default function ImageCanvas() {
|
|||||||
isSelected={label.id === selectedLabelId}
|
isSelected={label.id === selectedLabelId}
|
||||||
onSelect={() => setSelectedLabelId(label.id)}
|
onSelect={() => setSelectedLabelId(label.id)}
|
||||||
info={label}
|
info={label}
|
||||||
|
setLabel={setLabel(label.id)}
|
||||||
dragLayer={dragLayerRef.current as Konva.Layer}
|
dragLayer={dragLayerRef.current as Konva.Layer}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -312,7 +334,7 @@ export default function ImageCanvas() {
|
|||||||
|
|
||||||
<Layer ref={dragLayerRef} />
|
<Layer ref={dragLayerRef} />
|
||||||
</Stage>
|
</Stage>
|
||||||
<CanvasControlBar />
|
<CanvasControlBar saveJson={saveJson} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div></div>
|
<div></div>
|
||||||
|
@ -5,15 +5,7 @@ import { uploadImageFolder } from '@/api/imageApi';
|
|||||||
import useAuthStore from '@/stores/useAuthStore';
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
|
|
||||||
export default function ImageFolderUploadForm({
|
export default function ImageUploadFolderForm({ onClose, projectId }: { onClose: () => void; projectId: number }) {
|
||||||
onClose,
|
|
||||||
projectId,
|
|
||||||
parentId,
|
|
||||||
}: {
|
|
||||||
onClose: () => void;
|
|
||||||
projectId: number;
|
|
||||||
parentId: number;
|
|
||||||
}) {
|
|
||||||
const profile = useAuthStore((state) => state.profile);
|
const profile = useAuthStore((state) => state.profile);
|
||||||
const memberId = profile?.id || 0;
|
const memberId = profile?.id || 0;
|
||||||
|
|
||||||
@ -57,7 +49,7 @@ export default function ImageFolderUploadForm({
|
|||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
|
|
||||||
await uploadImageFolder(memberId, projectId, files, parentId)
|
await uploadImageFolder(memberId, projectId, files)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setProgress(100);
|
setProgress(100);
|
||||||
})
|
})
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import ImageFolderUploadForm from './ImageFolderUploadForm';
|
import ImageUploadFolderForm from './ImageUploadFolderForm';
|
||||||
|
|
||||||
export default function ImageFolderUploadModal({ projectId, parentId = 0 }: { projectId: number; parentId: number }) {
|
export default function ImageUploadFolderModal({ projectId }: { projectId: number }) {
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
|
||||||
const handleOpen = () => setIsOpen(true);
|
const handleOpen = () => setIsOpen(true);
|
||||||
@ -24,10 +24,9 @@ export default function ImageFolderUploadModal({ projectId, parentId = 0 }: { pr
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-w-2xl">
|
||||||
<DialogHeader title="폴더 업로드" />
|
<DialogHeader title="폴더 업로드" />
|
||||||
<ImageFolderUploadForm
|
<ImageUploadFolderForm
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
parentId={parentId}
|
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
@ -1,19 +1,11 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { uploadImageFolderZip } from '@/api/imageApi';
|
import { uploadImageZip } from '@/api/imageApi';
|
||||||
import useAuthStore from '@/stores/useAuthStore';
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
|
|
||||||
export default function ImageFolderZipUploadForm({
|
export default function ImageUploadZipForm({ onClose, projectId }: { onClose: () => void; projectId: number }) {
|
||||||
onClose,
|
|
||||||
projectId,
|
|
||||||
parentId,
|
|
||||||
}: {
|
|
||||||
onClose: () => void;
|
|
||||||
projectId: number;
|
|
||||||
parentId: number;
|
|
||||||
}) {
|
|
||||||
const profile = useAuthStore((state) => state.profile);
|
const profile = useAuthStore((state) => state.profile);
|
||||||
const memberId = profile?.id || 0;
|
const memberId = profile?.id || 0;
|
||||||
|
|
||||||
@ -58,7 +50,7 @@ export default function ImageFolderZipUploadForm({
|
|||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
|
|
||||||
await uploadImageFolderZip(memberId, projectId, file, parentId)
|
await uploadImageZip(memberId, projectId, file)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setProgress(100);
|
setProgress(100);
|
||||||
})
|
})
|
@ -1,15 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import ImageFolderZipUploadForm from './ImageFolderZipUploadForm';
|
import ImageUploadZipForm from './ImageUploadZipForm';
|
||||||
|
|
||||||
export default function ImageFolderZipUploadModal({
|
export default function ImageUploadZipModal({ projectId }: { projectId: number }) {
|
||||||
projectId,
|
|
||||||
parentId = 0,
|
|
||||||
}: {
|
|
||||||
projectId: number;
|
|
||||||
parentId: number;
|
|
||||||
}) {
|
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
|
||||||
const handleOpen = () => setIsOpen(true);
|
const handleOpen = () => setIsOpen(true);
|
||||||
@ -30,10 +24,9 @@ export default function ImageFolderZipUploadModal({
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-w-2xl">
|
||||||
<DialogHeader title="폴더 압축파일 업로드" />
|
<DialogHeader title="폴더 압축파일 업로드" />
|
||||||
<ImageFolderZipUploadForm
|
<ImageUploadZipForm
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
parentId={parentId}
|
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
@ -1,9 +1,10 @@
|
|||||||
import { Project } from '@/types';
|
import { Project } from '@/types';
|
||||||
import { SquarePenIcon, Upload } from 'lucide-react';
|
import { Play, SquarePenIcon, Upload } from 'lucide-react';
|
||||||
import ProjectFileItem from './ProjectFileItem';
|
import ProjectFileItem from './ProjectFileItem';
|
||||||
import ProjectDirectoryItem from './ProjectDirectoryItem';
|
import ProjectDirectoryItem from './ProjectDirectoryItem';
|
||||||
import useFolderQuery from '@/queries/folders/useFolderQuery';
|
import useFolderQuery from '@/queries/folders/useFolderQuery';
|
||||||
import useCanvasStore from '@/stores/useCanvasStore';
|
import useCanvasStore from '@/stores/useCanvasStore';
|
||||||
|
import { Button } from '../ui/button';
|
||||||
|
|
||||||
export default function ProjectStructure({ project }: { project: Project }) {
|
export default function ProjectStructure({ project }: { project: Project }) {
|
||||||
const image = useCanvasStore((state) => state.image);
|
const image = useCanvasStore((state) => state.image);
|
||||||
@ -22,46 +23,62 @@ export default function ProjectStructure({ project }: { project: Project }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col overflow-y-auto px-1 pb-2">
|
<div className="flex h-full flex-col justify-between">
|
||||||
<header className="flex w-full items-center gap-2 rounded p-1">
|
<div className="flex flex-col overflow-y-auto px-1 pb-2">
|
||||||
<div className="flex w-full items-center gap-1 overflow-hidden pr-1">
|
<header className="flex w-full items-center gap-2 rounded p-1">
|
||||||
<h3 className="caption overflow-hidden text-ellipsis whitespace-nowrap">{project.type}</h3>
|
<div className="flex w-full items-center gap-1 overflow-hidden pr-1">
|
||||||
</div>
|
<h3 className="caption overflow-hidden text-ellipsis whitespace-nowrap">{project.type}</h3>
|
||||||
<button
|
</div>
|
||||||
className="flex gap-1"
|
<button
|
||||||
onClick={() => console.log('edit project')}
|
className="flex gap-1"
|
||||||
|
onClick={() => console.log('edit project')}
|
||||||
|
>
|
||||||
|
<SquarePenIcon size={16} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="flex gap-1"
|
||||||
|
onClick={() => console.log('upload image')}
|
||||||
|
>
|
||||||
|
<Upload size={16} />
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
{folderData.children.length === 0 && folderData.images.length === 0 ? (
|
||||||
|
<div className="body-small flex h-full select-none items-center justify-center text-gray-400">
|
||||||
|
빈 프로젝트입니다.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="caption flex flex-col">
|
||||||
|
{folderData.children.map((item) => (
|
||||||
|
<ProjectDirectoryItem
|
||||||
|
key={`${project.id}-${item.title}`}
|
||||||
|
item={item}
|
||||||
|
initialExpanded={true}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{folderData.images.map((item) => (
|
||||||
|
<ProjectFileItem
|
||||||
|
key={`${project.id}-${item.imageTitle}`}
|
||||||
|
item={item}
|
||||||
|
selected={image?.id === item.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex p-2.5">
|
||||||
|
<Button
|
||||||
|
variant="outlinePrimary"
|
||||||
|
className="w-full"
|
||||||
|
onClick={() => console.log('autolabel')}
|
||||||
>
|
>
|
||||||
<SquarePenIcon size={16} />
|
<Play
|
||||||
</button>
|
size={16}
|
||||||
<button
|
className="mr-1"
|
||||||
className="flex gap-1"
|
/>
|
||||||
onClick={() => console.log('upload image')}
|
<span>자동 레이블링</span>
|
||||||
>
|
</Button>
|
||||||
<Upload size={16} />
|
</div>
|
||||||
</button>
|
|
||||||
</header>
|
|
||||||
{folderData.children.length === 0 && folderData.images.length === 0 ? (
|
|
||||||
<div className="body-small flex h-full select-none items-center justify-center text-gray-400">
|
|
||||||
빈 프로젝트입니다.
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="caption flex flex-col">
|
|
||||||
{folderData.children.map((item) => (
|
|
||||||
<ProjectDirectoryItem
|
|
||||||
key={`${project.id}-${item.title}`}
|
|
||||||
item={item}
|
|
||||||
initialExpanded={true}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{folderData.images.map((item) => (
|
|
||||||
<ProjectFileItem
|
|
||||||
key={`${project.id}-${item.imageTitle}`}
|
|
||||||
item={item}
|
|
||||||
selected={image?.id === item.id}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ export default function WorkspaceSidebar({ workspaceName, projects }: { workspac
|
|||||||
const setSidebarSize = useCanvasStore((state) => state.setSidebarSize);
|
const setSidebarSize = useCanvasStore((state) => state.setSidebarSize);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { workspaceId } = useParams<{ workspaceId: string }>();
|
const { workspaceId } = useParams<{ workspaceId: string }>();
|
||||||
// const [selectedProjectId, setSelectedProjectId] = useState<string | undefined>();
|
|
||||||
const handleSelectProject = (projectId: string) => {
|
const handleSelectProject = (projectId: string) => {
|
||||||
// setSelectedProjectId(projectId);
|
|
||||||
navigate(`${webPath.workspace()}/${workspaceId}/${projectId}`);
|
navigate(`${webPath.workspace()}/${workspaceId}/${projectId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import ImageFolderUploadModal from '@/components/ImageFolderUploadModal';
|
import ImageUploadFolderModal from '@/components/ImageUploadFolderModal';
|
||||||
import ImageFolderZipUploadModal from '@/components/ImageFolderZipUploadModal';
|
import ImageUploadZipModal from '@/components/ImageUploadZipModal';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
export default function ImageFolderUploadTest() {
|
export default function ImageFolderUploadTest() {
|
||||||
@ -8,14 +8,8 @@ export default function ImageFolderUploadTest() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen w-full">
|
<div className="min-h-screen w-full">
|
||||||
<ImageFolderUploadModal
|
<ImageUploadFolderModal projectId={projectId} />
|
||||||
projectId={projectId}
|
<ImageUploadZipModal projectId={projectId} />
|
||||||
parentId={0}
|
|
||||||
/>
|
|
||||||
<ImageFolderZipUploadModal
|
|
||||||
projectId={projectId}
|
|
||||||
parentId={0}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user