Merge branch 'fe/develop' of https://lab.ssafy.com/s11-s-project/S11P21S002 into fe/refactor/labling-page
This commit is contained in:
commit
5edfe33564
@ -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,
|
||||||
|
@ -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';
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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',
|
||||||
|
@ -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 }),
|
||||||
|
Loading…
Reference in New Issue
Block a user