Merge branch 'fe/develop' of https://lab.ssafy.com/s11-s-project/S11P21S002 into fe/refactor/labling-page

This commit is contained in:
정현조 2024-09-13 09:51:52 +09:00
commit 5edfe33564
6 changed files with 64 additions and 72 deletions

View File

@ -2,8 +2,7 @@ import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'ax
import useAuthStore from '@/stores/useAuthStore'; import useAuthStore from '@/stores/useAuthStore';
import { BaseResponse, CustomError, SuccessResponse, RefreshTokenResponseDTO } from '@/types'; import { BaseResponse, CustomError, SuccessResponse, RefreshTokenResponseDTO } from '@/types';
const baseURL = 'https://j11s002.p.ssafy.io'; const baseURL = import.meta.env.VITE_API_URL;
// const baseURL = 'http://localhost:5173'; 모킹 테스트
const api = axios.create({ const api = axios.create({
baseURL, baseURL,

View File

@ -3,42 +3,15 @@ import Konva from 'konva';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { Circle, Image, Layer, Line, Rect, Stage } from 'react-konva'; import { Circle, Image, Layer, Line, Rect, Stage } from 'react-konva';
import useImage from 'use-image'; import useImage from 'use-image';
import { Label } from '@/types';
import LabelRect from './LabelRect'; import LabelRect from './LabelRect';
import { Vector2d } from 'konva/lib/types'; import { Vector2d } from 'konva/lib/types';
import LabelPolygon from './LabelPolygon'; import LabelPolygon from './LabelPolygon';
import CanvasControlBar from '../CanvasControlBar'; import CanvasControlBar from '../CanvasControlBar';
const mockLabels: Label[] = Array.from({ length: 10 }, (_, i) => {
const startX = Math.random() * 1200 + 300;
const startY = Math.random() * 2000 + 300;
const color = Math.floor(Math.random() * 65535)
.toString(16)
.padStart(4, '0');
return {
id: i,
name: `label-${i}`,
type: i % 2 === 0 ? 'polygon' : 'rect',
color: i % 2 === 0 ? `#ff${color}` : `#${color}ff`,
coordinates:
i % 2 === 0
? [
[startX, startY],
[startX + 200, startY + 50],
[startX + 300, startY + 300],
[startX + 100, startY + 250],
]
: [
[startX, startY],
[startX + 300, startY + 300],
],
};
});
export default function ImageCanvas() { export default function ImageCanvas() {
const stageWidth = window.innerWidth; const sidebarSize = useCanvasStore((state) => state.sidebarSize);
const stageHeight = window.innerHeight; const stageWidth = window.innerWidth * ((100 - sidebarSize) / 100) - 280;
const stageHeight = window.innerHeight - 64;
const stageRef = useRef<Konva.Stage>(null); const stageRef = useRef<Konva.Stage>(null);
const dragLayerRef = useRef<Konva.Layer>(null); const dragLayerRef = useRef<Konva.Layer>(null);
const scale = useRef<number>(0); const scale = useRef<number>(0);
@ -100,7 +73,7 @@ export default function ImageCanvas() {
const color = Math.floor(Math.random() * 65535) const color = Math.floor(Math.random() * 65535)
.toString(16) .toString(16)
.padStart(6, '0'); .padStart(6, '0');
const id = labels.length; const id = labels.length + 1;
addLabel({ addLabel({
id: id, id: id,
name: 'label', name: 'label',
@ -127,7 +100,7 @@ export default function ImageCanvas() {
const color = Math.floor(Math.random() * 65535) const color = Math.floor(Math.random() * 65535)
.toString(16) .toString(16)
.padStart(6, '0'); .padStart(6, '0');
const id = labels.length; const id = labels.length + 1;
addLabel({ addLabel({
id: id, id: id,
name: 'label', name: 'label',
@ -209,11 +182,6 @@ export default function ImageCanvas() {
return { x: scale.current, y: scale.current }; return { x: scale.current, y: scale.current };
}; };
// TODO: remove mock data
useEffect(() => {
useCanvasStore.setState({ labels: mockLabels });
}, []);
useEffect(() => { useEffect(() => {
if (!stageRef.current) return; if (!stageRef.current) return;
stageRef.current.container().style.cursor = drawState === 'pointer' ? 'default' : 'crosshair'; stageRef.current.container().style.cursor = drawState === 'pointer' ? 'default' : 'crosshair';

View File

@ -9,8 +9,47 @@ import { fetchFolderApi } from '@/api/folderApi';
import { getWorkspaceApi } from '@/api/workspaceApi'; import { getWorkspaceApi } from '@/api/workspaceApi';
import { getAllProjectsApi } from '@/api/projectApi'; import { getAllProjectsApi } from '@/api/projectApi';
import useAuthStore from '@/stores/useAuthStore'; import useAuthStore from '@/stores/useAuthStore';
import useCanvasStore from '@/stores/useCanvasStore';
const mockLabels: Label[] = [
{
id: 1,
name: 'Label 1',
color: '#FFaa33',
coordinates: [
[700, 100],
[1200, 800],
],
type: 'rect',
},
{
id: 2,
name: 'Label 2',
color: '#aaFF55',
coordinates: [
[200, 200],
[400, 200],
[500, 500],
[400, 800],
[200, 800],
[100, 500],
],
type: 'polygon',
},
{
id: 3,
name: 'Label 3',
color: '#77aaFF',
coordinates: [
[1000, 1000],
[1800, 1800],
],
type: 'rect',
},
];
export default function WorkspaceLayout() { export default function WorkspaceLayout() {
const setLabels = useCanvasStore((state) => state.setLabels);
const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>(); const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();
const [workspace, setWorkspace] = useState<{ name: string; projects: Project[] }>({ const [workspace, setWorkspace] = useState<{ name: string; projects: Project[] }>({
name: '', name: '',
@ -109,29 +148,9 @@ export default function WorkspaceLayout() {
} }
}, [workspaceId, projectId, memberId]); }, [workspaceId, projectId, memberId]);
const labels: Label[] = [ useEffect(() => {
{ setLabels(mockLabels);
id: 1, }, [setLabels]);
name: 'Label 1',
color: '#FFaa33',
coordinates: [],
type: 'rect',
},
{
id: 2,
name: 'Label 2',
color: '#aaFF55',
coordinates: [],
type: 'rect',
},
{
id: 3,
name: 'Label 3',
color: '#77aaFF',
coordinates: [],
type: 'rect',
},
];
return ( return (
<> <>
@ -146,7 +165,7 @@ export default function WorkspaceLayout() {
<main className="h-full grow"> <main className="h-full grow">
<Outlet /> <Outlet />
</main> </main>
<WorkspaceLabelBar labels={labels} /> <WorkspaceLabelBar labels={mockLabels} />
</ResizablePanel> </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>
</div> </div>

View File

@ -6,12 +6,13 @@ import ProjectStructure from './ProjectStructure';
import { Project } from '@/types'; import { Project } from '@/types';
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '../ui/select'; import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '../ui/select';
import ProjectCreateModal from '../ProjectCreateModal'; import ProjectCreateModal from '../ProjectCreateModal';
import useCanvasStore from '@/stores/useCanvasStore';
export default function WorkspaceSidebar({ workspaceName, projects }: { workspaceName: string; projects: Project[] }) { export default function WorkspaceSidebar({ workspaceName, projects }: { workspaceName: string; projects: Project[] }) {
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 [selectedProjectId, setSelectedProjectId] = useState<string | undefined>();
const handleSelectProject = (projectId: string) => { const handleSelectProject = (projectId: string) => {
setSelectedProjectId(projectId); setSelectedProjectId(projectId);
navigate(`/workspace/${workspaceId}/project/${projectId}`); navigate(`/workspace/${workspaceId}/project/${projectId}`);
@ -24,9 +25,7 @@ export default function WorkspaceSidebar({ workspaceName, projects }: { workspac
maxSize={35} maxSize={35}
defaultSize={20} defaultSize={20}
className="flex h-full flex-col bg-gray-100" className="flex h-full flex-col bg-gray-100"
onResize={(size) => { onResize={(size) => setSidebarSize(size)}
console.log(size);
}}
> >
<header className="body flex w-full items-center gap-2 p-2"> <header className="body flex w-full items-center gap-2 p-2">
<h1 className="w-full overflow-hidden text-ellipsis whitespace-nowrap">{workspaceName}</h1> <h1 className="w-full overflow-hidden text-ellipsis whitespace-nowrap">{workspaceName}</h1>

View File

@ -14,8 +14,9 @@ import { Navigate } from 'react-router-dom';
export const webPath = { export const webPath = {
home: () => '/', home: () => '/',
browse: () => '/browse', browse: () => '/browse',
workspace: (workspaceId: string, projectId?: string) => workspace: () => '/workspace',
projectId ? `/workspace/${workspaceId}/project/${projectId}` : `/workspace/${workspaceId}`, // workspace: (workspaceId: string, projectId?: string) =>
// projectId ? `/workspace/${workspaceId}/project/${projectId}` : `/workspace/${workspaceId}`,
admin: (workspaceId: string) => `/admin/${workspaceId}`, admin: (workspaceId: string) => `/admin/${workspaceId}`,
oauthCallback: () => '/redirect/oauth2', oauthCallback: () => '/redirect/oauth2',
}; };
@ -46,12 +47,12 @@ const router = createBrowserRouter([
], ],
}, },
{ {
path: '/workspace/:workspaceId', path: `${webPath.workspace()}/:workspaceId`,
element: <WorkspaceLayout />, element: <WorkspaceLayout />,
children: [ children: [
{ {
path: '', index: true,
element: <div>workspace</div>, element: <ImageCanvas />,
}, },
{ {
path: 'project/:projectId', path: 'project/:projectId',

View File

@ -2,10 +2,13 @@ import { Label } from '@/types';
import { create } from 'zustand'; import { create } from 'zustand';
interface CanvasState { interface CanvasState {
sidebarSize: number;
image: string; image: string;
labels: Label[]; labels: Label[];
drawState: 'pen' | 'rect' | 'pointer'; drawState: 'pen' | 'rect' | 'pointer';
setSidebarSize: (width: number) => void;
changeImage: (image: string, labels: Label[]) => void; changeImage: (image: string, labels: Label[]) => void;
setLabels: (labels: Label[]) => void;
addLabel: (label: Label) => void; addLabel: (label: Label) => void;
removeLabel: (labelId: number) => void; removeLabel: (labelId: number) => void;
updateLabel: (label: Label) => void; updateLabel: (label: Label) => void;
@ -13,11 +16,14 @@ interface CanvasState {
} }
const useCanvasStore = create<CanvasState>()((set) => ({ const useCanvasStore = create<CanvasState>()((set) => ({
sidebarSize: 20,
image: '', image: '',
labels: [], labels: [],
drawState: 'pointer', drawState: 'pointer',
setSidebarSize: (width) => set({ sidebarSize: width }),
changeImage: (image: string, labels: Label[]) => set({ image, labels }), changeImage: (image: string, labels: Label[]) => set({ image, labels }),
addLabel: (label: Label) => set((state) => ({ labels: [...state.labels, label] })), addLabel: (label: Label) => set((state) => ({ labels: [...state.labels, label] })),
setLabels: (labels) => set({ labels }),
removeLabel: (labelId: number) => set((state) => ({ labels: state.labels.filter((label) => label.id !== labelId) })), removeLabel: (labelId: number) => set((state) => ({ labels: state.labels.filter((label) => label.id !== labelId) })),
updateLabel: (label: Label) => set((state) => ({ labels: state.labels.map((l) => (l.id === label.id ? label : l)) })), updateLabel: (label: Label) => set((state) => ({ labels: state.labels.map((l) => (l.id === label.id ? label : l)) })),
setDrawState: (drawState) => set({ drawState }), setDrawState: (drawState) => set({ drawState }),