Refactor: 레이블링 페이지 레이아웃 변경

This commit is contained in:
정현조 2024-09-13 08:23:15 +09:00
parent ebc548a1d9
commit 7ca4012f68
3 changed files with 161 additions and 70 deletions

View File

@ -1,65 +1,114 @@
import { Outlet } from 'react-router-dom'; import { useEffect, useState } from 'react';
import { useParams, Outlet } from 'react-router-dom';
import Header from '../Header'; import Header from '../Header';
import { Label, Project } from '@/types'; import { Label, Project, DirectoryItem, FileItem } from '@/types';
import { ResizablePanelGroup, ResizablePanel } from '../ui/resizable'; import { ResizablePanelGroup, ResizablePanel } from '../ui/resizable';
import WorkspaceSidebar from '../WorkspaceSidebar'; import WorkspaceSidebar from '../WorkspaceSidebar';
import WorkspaceLabelBar from '../WorkspaceLabelBar'; import WorkspaceLabelBar from '../WorkspaceLabelBar';
import { fetchFolderApi } from '@/api/folderApi';
import { getWorkspaceApi } from '@/api/workspaceApi';
import { getAllProjectsApi } from '@/api/projectApi';
import useAuthStore from '@/stores/useAuthStore';
export default function WorkspaceLayout() { export default function WorkspaceLayout() {
const workspace: { name: string; projects: Project[] } = { const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();
name: 'Workspace-name-1', const [workspace, setWorkspace] = useState<{ name: string; projects: Project[] }>({
projects: [ name: '',
{ projects: [],
id: 1, });
name: 'project-111', const profile = useAuthStore((state) => state.profile);
type: 'Segmentation', const memberId = profile?.id || 0;
children: [
{ useEffect(() => {
id: 12, const fetchWorkspaceData = async (workspaceId: number, memberId: number) => {
type: 'directory', try {
name: 'directory-1', const workspaceResponse = await getWorkspaceApi(workspaceId, memberId);
children: [ if (workspaceResponse.isSuccess) {
{ const workspaceTitle = workspaceResponse.data.title;
id: 123, setWorkspace((prev) => ({
type: 'directory', ...prev,
name: 'directory-2', name: workspaceTitle,
children: [ }));
{ id: 1, url: '', type: 'image', name: 'image-1.jpg', status: 'done' }, fetchProjects(workspaceId, memberId);
{ id: 1, url: '', type: 'image', name: 'image-2.jpg', status: 'idle' }, }
], } catch (error) {
}, console.error('워크스페이스 조회 실패:', error);
{ id: 1, url: '', type: 'image', name: 'image-1.jpg', status: 'idle' }, }
{ id: 1, url: '', type: 'image', name: 'image-2.jpg', status: 'done' }, };
],
}, const fetchProjects = async (workspaceId: number, memberId: number) => {
{ id: 1, url: '', type: 'image', name: 'image-1.jpg', status: 'done' }, try {
], const projectResponse = await getAllProjectsApi(workspaceId, memberId);
}, if (projectResponse.isSuccess) {
{ const projects = await Promise.all(
id: 2, projectResponse.data.workspaceResponses.map(async (project) => {
name: 'very-extremely-long-long-project-name-222', const children = await fetchFolderWithImages(project.id, memberId);
type: 'Classification', return {
children: [ id: project.id,
{ name: project.title,
id: 23, type: capitalizeType(project.projectType),
type: 'directory', children,
name: 'this-is-my-very-very-long-directory-name-that-will-be-overflow', };
children: [ })
{ id: 1, url: '', type: 'image', name: 'image-1.jpg', status: 'done' }, );
{ id: 1, url: '', type: 'image', name: 'image-2.jpg', status: 'done' }, setWorkspace((prev) => ({
], ...prev,
}, projects: projects as Project[],
{ }));
id: 1, }
url: '', } catch (error) {
console.error('프로젝트 목록 조회 실패:', error);
}
};
const fetchFolderWithImages = async (projectId: number, memberId: number): Promise<DirectoryItem[]> => {
try {
const folderResponse = await fetchFolderApi(projectId, 0, memberId);
if (folderResponse.isSuccess) {
const files: FileItem[] = folderResponse.data.images.map((image) => ({
id: image.id,
name: image.imageTitle,
url: image.imageUrl,
type: 'image', type: 'image',
name: 'wow-this-is-my-very-very-long-image-name-so-this-will-be-overflow-too.jpg', status: image.status === 'COMPLETED' ? 'done' : 'idle',
status: 'idle', }));
},
], return [
}, {
], id: folderResponse.data.id,
}; name: folderResponse.data.title,
type: 'directory',
children: files,
},
];
}
return [];
} catch (error) {
console.error('폴더 및 이미지 조회 실패:', error);
return [];
}
};
const capitalizeType = (
type: 'classification' | 'detection' | 'segmentation'
): 'Classification' | 'Detection' | 'Segmentation' => {
switch (type) {
case 'classification':
return 'Classification';
case 'detection':
return 'Detection';
case 'segmentation':
return 'Segmentation';
default:
throw new Error(`Unknown project type: ${type}`);
}
};
if (workspaceId && memberId) {
fetchWorkspaceData(Number(workspaceId), memberId);
}
}, [workspaceId, projectId, memberId]);
const labels: Label[] = [ const labels: Label[] = [
{ {
id: 1, id: 1,

View File

@ -1,6 +1,7 @@
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { FileItem } from '@/types'; import { FileItem } from '@/types';
import { Check, Image, Minus } from 'lucide-react'; import { Check, Image, Minus } from 'lucide-react';
import useCanvasStore from '@/stores/useCanvasStore';
export default function ProjectFileItem({ export default function ProjectFileItem({
className = '', className = '',
@ -12,6 +13,19 @@ export default function ProjectFileItem({
depth?: number; depth?: number;
}) { }) {
const paddingLeft = depth * 12; const paddingLeft = depth * 12;
const changeImage = useCanvasStore((state) => state.changeImage);
const handleClick = () => {
changeImage(item.url, [
{
id: item.id,
name: item.name,
type: 'rect',
color: '#FF0000',
coordinates: [],
},
]);
};
return ( return (
<button <button
@ -19,6 +33,7 @@ export default function ProjectFileItem({
style={{ style={{
paddingLeft, paddingLeft,
}} }}
onClick={handleClick}
> >
<div className="flex items-center"> <div className="flex items-center">
<Image <Image

View File

@ -1,10 +1,22 @@
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { SquarePen } from 'lucide-react'; import { SquarePen } from 'lucide-react';
import { ResizableHandle, ResizablePanel } from '../ui/resizable'; import { ResizableHandle, ResizablePanel } from '../ui/resizable';
import ProjectStructure from './ProjectStructure'; import ProjectStructure from './ProjectStructure';
import { Button } from '../ui/button';
import { Project } from '@/types'; import { Project } from '@/types';
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '../ui/select';
import ProjectCreateModal from '../ProjectCreateModal';
export default function WorkspaceSidebar({ workspaceName, projects }: { workspaceName: string; projects: Project[] }) { export default function WorkspaceSidebar({ workspaceName, projects }: { workspaceName: string; projects: Project[] }) {
const navigate = useNavigate();
const { workspaceId } = useParams<{ workspaceId: string }>();
const [selectedProjectId, setSelectedProjectId] = useState<string | undefined>();
const handleSelectProject = (projectId: string) => {
setSelectedProjectId(projectId);
navigate(`/workspace/${workspaceId}/project/${projectId}`);
};
return ( return (
<> <>
<ResizablePanel <ResizablePanel
@ -21,22 +33,37 @@ export default function WorkspaceSidebar({ workspaceName, projects }: { workspac
<button> <button>
<SquarePen size={16} /> <SquarePen size={16} />
</button> </button>
<Button <ProjectCreateModal
variant="outline" onSubmit={(data) => console.log('프로젝트 생성:', data)}
size="xs" buttonClass="caption border-gray-800 bg-gray-100 flex items-center gap-2"
className="caption border-gray-800 bg-gray-100" />
onClick={() => console.log('New project')}
>
</Button>
</header> </header>
<div className="p-2">
<Select onValueChange={handleSelectProject}>
<SelectTrigger>
<SelectValue placeholder="프로젝트를 선택해주세요" />
</SelectTrigger>
<SelectContent>
{projects.map((project) => (
<SelectItem
key={project.id}
value={project.id.toString()}
>
{project.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div> <div>
{projects.map((project) => ( {projects
<ProjectStructure .filter((project) => project.id.toString() === selectedProjectId)
key={project.id} .map((project) => (
project={project} <ProjectStructure
/> key={project.id}
))} project={project}
/>
))}
</div> </div>
</ResizablePanel> </ResizablePanel>
<ResizableHandle className="bg-gray-300" /> <ResizableHandle className="bg-gray-300" />