refactor: 수정된 response 형식 반영
This commit is contained in:
parent
503976b3ee
commit
6076c7cea1
@ -1,6 +1,6 @@
|
||||
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import useAuthStore from '@/stores/useAuthStore';
|
||||
import { BaseResponse, CustomError, SuccessResponse, RefreshTokenResponseDTO } from '@/types';
|
||||
import { BaseResponse, CustomError, SuccessResponse, RefreshTokenResponse } from '@/types';
|
||||
|
||||
const baseURL = import.meta.env.VITE_API_URL;
|
||||
|
||||
@ -60,11 +60,9 @@ api.interceptors.response.use(
|
||||
isTokenRefreshing = true;
|
||||
|
||||
try {
|
||||
const response: AxiosResponse<SuccessResponse<RefreshTokenResponseDTO>> = await api.post(
|
||||
'/api/auth/reissue',
|
||||
null,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
const response: AxiosResponse<SuccessResponse<RefreshTokenResponse>> = await api.post('/auth/reissue', null, {
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
const newAccessToken = response.data.data?.accessToken;
|
||||
if (!newAccessToken) {
|
||||
|
@ -32,17 +32,21 @@ export async function changeImageStatus(
|
||||
memberId: number,
|
||||
statusChangeRequest: ImageStatusChangeRequest
|
||||
) {
|
||||
return api.put(`/projects/${projectId}/folders/${folderId}/images/${imageId}/status`, statusChangeRequest, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.put(`/projects/${projectId}/folders/${folderId}/images/${imageId}/status`, statusChangeRequest, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function uploadImageList(projectId: number, folderId: number, memberId: number, imageList: string[]) {
|
||||
return api.post(
|
||||
`/projects/${projectId}/folders/${folderId}/images`,
|
||||
{ imageList },
|
||||
{
|
||||
params: { memberId },
|
||||
}
|
||||
);
|
||||
return api
|
||||
.post(
|
||||
`/projects/${projectId}/folders/${folderId}/images`,
|
||||
{ imageList },
|
||||
{
|
||||
params: { memberId },
|
||||
}
|
||||
)
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
@ -2,17 +2,21 @@ import api from '@/api/axiosConfig';
|
||||
import { LabelingRequest } from '@/types';
|
||||
|
||||
export async function saveImageLabels(projectId: number, imageId: number, memberId: number, data: LabelingRequest) {
|
||||
return api.post(`/projects/${projectId}/label/image/${imageId}`, data, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.post(`/projects/${projectId}/label/image/${imageId}`, data, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function runAutoLabel(projectId: number, memberId: number) {
|
||||
return api.post(
|
||||
`/projects/${projectId}/label/auto`,
|
||||
{},
|
||||
{
|
||||
params: { memberId },
|
||||
}
|
||||
);
|
||||
return api
|
||||
.post(
|
||||
`/projects/${projectId}/label/auto`,
|
||||
{},
|
||||
{
|
||||
params: { memberId },
|
||||
}
|
||||
)
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
@ -1,8 +1,26 @@
|
||||
import api from '@/api/axiosConfig';
|
||||
import { ProjectListResponse, ProjectResponse } from '@/types';
|
||||
|
||||
export async function getProjectList(
|
||||
workspaceId: number,
|
||||
memberId: number,
|
||||
lastProjectId?: number,
|
||||
limit: number = 10
|
||||
) {
|
||||
return api
|
||||
.get<ProjectListResponse>(`/workspaces/${workspaceId}/projects`, {
|
||||
params: {
|
||||
memberId,
|
||||
lastProjectId,
|
||||
limit,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function getProject(projectId: number, memberId: number) {
|
||||
return api
|
||||
.get(`/projects/${projectId}`, {
|
||||
.get<ProjectResponse>(`/projects/${projectId}`, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
@ -54,23 +72,6 @@ export async function removeProjectMember(projectId: number, memberId: number, t
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function getAllProjects(
|
||||
workspaceId: number,
|
||||
memberId: number,
|
||||
lastProjectId?: number,
|
||||
limit: number = 10
|
||||
) {
|
||||
return api
|
||||
.get(`/workspaces/${workspaceId}/projects`, {
|
||||
params: {
|
||||
memberId,
|
||||
lastProjectId,
|
||||
limit,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function createProject(
|
||||
workspaceId: number,
|
||||
memberId: number,
|
||||
|
@ -1,44 +1,58 @@
|
||||
import api from '@/api/axiosConfig';
|
||||
import { WorkspaceRequest } from '@/types';
|
||||
import { WorkspaceListResponse, WorkspaceRequest, WorkspaceResponse } from '@/types';
|
||||
|
||||
export async function fetchWorkspaceList(memberId: number, lastWorkspaceId?: number, limit?: number) {
|
||||
return api.get('/workspaces', {
|
||||
params: { memberId, lastWorkspaceId, limit },
|
||||
});
|
||||
export async function getWorkspaceList(memberId: number, lastWorkspaceId?: number, limit?: number) {
|
||||
return api
|
||||
.get<WorkspaceListResponse>('/workspaces', {
|
||||
params: { memberId, lastWorkspaceId, limit },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function fetchWorkspace(workspaceId: number, memberId: number) {
|
||||
return api.get(`/workspaces/${workspaceId}`, {
|
||||
params: { memberId },
|
||||
});
|
||||
export async function getWorkspace(workspaceId: number, memberId: number) {
|
||||
return api
|
||||
.get<WorkspaceResponse>(`/workspaces/${workspaceId}`, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function updateWorkspace(workspaceId: number, memberId: number, data: WorkspaceRequest) {
|
||||
return api.put(`/workspaces/${workspaceId}`, data, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.put(`/workspaces/${workspaceId}`, data, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function deleteWorkspace(workspaceId: number, memberId: number) {
|
||||
return api.delete(`/workspaces/${workspaceId}`, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.delete(`/workspaces/${workspaceId}`, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function createWorkspace(memberId: number, data: WorkspaceRequest) {
|
||||
return api.post('/workspaces', data, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.post('/workspaces', data, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function addWorkspaceMember(workspaceId: number, memberId: number, newMemberId: number) {
|
||||
return api.post(`/workspaces/${workspaceId}/members/${newMemberId}`, null, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.post(`/workspaces/${workspaceId}/members/${newMemberId}`, null, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
||||
export async function removeWorkspaceMember(workspaceId: number, memberId: number, targetMemberId: number) {
|
||||
return api.delete(`/workspaces/${workspaceId}/members/${targetMemberId}`, {
|
||||
params: { memberId },
|
||||
});
|
||||
return api
|
||||
.delete(`/workspaces/${workspaceId}/members/${targetMemberId}`, {
|
||||
params: { memberId },
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
}
|
||||
|
@ -1,43 +1,26 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useAuthStore from '@/stores/useAuthStore';
|
||||
import { fetchProfileApi } from '@/api/authApi';
|
||||
import useProfileQuery from '@/queries/useProfileQuery';
|
||||
|
||||
export default function OAuthCallback() {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const accessToken = queryParams.get('accessToken');
|
||||
const navigate = useNavigate();
|
||||
const setLoggedIn = useAuthStore((state) => state.setLoggedIn);
|
||||
const setProfile = useAuthStore((state) => state.setProfile);
|
||||
const { data: profile } = useProfileQuery();
|
||||
|
||||
useEffect(() => {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const accessToken = queryParams.get('accessToken');
|
||||
|
||||
if (accessToken) {
|
||||
setLoggedIn(true, accessToken);
|
||||
|
||||
fetchProfileApi()
|
||||
.then((data) => {
|
||||
if (data?.isSuccess && data.data) {
|
||||
const profileData = {
|
||||
id: data.data.id,
|
||||
nickname: data.data.nickname,
|
||||
profileImage: data.data.profileImage,
|
||||
};
|
||||
setProfile(profileData);
|
||||
navigate('/browse');
|
||||
} else {
|
||||
throw new Error('프로필 데이터를 가져올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert('프로필을 가져오는 중 오류가 발생했습니다. 다시 로그인해주세요.');
|
||||
console.error('프로필 가져오기 실패:', error);
|
||||
navigate('/');
|
||||
});
|
||||
} else {
|
||||
if (!accessToken) {
|
||||
navigate('/');
|
||||
return;
|
||||
}
|
||||
}, [navigate, setLoggedIn, setProfile]);
|
||||
|
||||
setLoggedIn(true, accessToken);
|
||||
setProfile(profile);
|
||||
navigate('/browse');
|
||||
}, [accessToken, navigate, profile, setLoggedIn, setProfile]);
|
||||
|
||||
return <p>처리 중입니다...</p>;
|
||||
}
|
||||
|
@ -2,10 +2,10 @@ import * as React from 'react';
|
||||
import WorkSpaceCreateForm, { WorkSpaceCreateFormValues } from './WorkSpaceCreateForm';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { WorkspaceRequestDTO } from '@/types';
|
||||
import { WorkspaceRequest } from '@/types';
|
||||
|
||||
interface WorkSpaceCreateModalProps {
|
||||
onSubmit: (data: WorkspaceRequestDTO) => void;
|
||||
onSubmit: (data: WorkspaceRequest) => void;
|
||||
}
|
||||
|
||||
export default function WorkSpaceCreateModal({ onSubmit }: WorkSpaceCreateModalProps) {
|
||||
@ -15,7 +15,7 @@ export default function WorkSpaceCreateModal({ onSubmit }: WorkSpaceCreateModalP
|
||||
const handleClose = () => setIsOpen(false);
|
||||
|
||||
const handleFormSubmit = (data: WorkSpaceCreateFormValues) => {
|
||||
const formattedData: WorkspaceRequestDTO = {
|
||||
const formattedData: WorkspaceRequest = {
|
||||
title: data.workspaceName,
|
||||
content: data.workspaceDescription || '',
|
||||
};
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { Suspense, useEffect } from 'react';
|
||||
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
|
||||
import Header from '../Header';
|
||||
import { useGetAllWorkspaces, useCreateWorkspace } from '@/hooks/useWorkspaceHooks';
|
||||
import useAuthStore from '@/stores/useAuthStore';
|
||||
import WorkSpaceCreateModal from '../WorkSpaceCreateModal';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
import { WorkspaceRequestDTO, WorkspaceResponseDTO, CustomError } from '@/types';
|
||||
import { WorkspaceRequest, WorkspaceResponse } from '@/types';
|
||||
import useWorkspaceListQuery from '@/queries/useWorkspaceListQuery';
|
||||
|
||||
export default function WorkspaceBrowseLayout() {
|
||||
const { profile, isLoggedIn } = useAuthStore();
|
||||
const memberId = profile?.id ?? 0;
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn || !memberId) {
|
||||
@ -21,26 +18,27 @@ export default function WorkspaceBrowseLayout() {
|
||||
}
|
||||
}, [isLoggedIn, memberId, navigate]);
|
||||
|
||||
const { data: workspacesResponse, isLoading, isError } = useGetAllWorkspaces(memberId || 0);
|
||||
const { data: workspacesResponse, isLoading, isError } = useWorkspaceListQuery(memberId ?? 0);
|
||||
|
||||
const workspaces = workspacesResponse?.data?.workspaceResponses || [];
|
||||
const workspaces = workspacesResponse?.workspaceResponses ?? [];
|
||||
|
||||
const createWorkspace = useCreateWorkspace();
|
||||
// const createWorkspace = useCreateWorkspace();
|
||||
|
||||
const handleCreateWorkspace = (data: WorkspaceRequestDTO) => {
|
||||
const handleCreateWorkspace = (data: WorkspaceRequest) => {
|
||||
if (!memberId) return;
|
||||
createWorkspace.mutate(
|
||||
{ memberId, data },
|
||||
{
|
||||
onSuccess: () => {
|
||||
console.log('워크스페이스가 성공적으로 생성되었습니다.');
|
||||
queryClient.invalidateQueries({ queryKey: ['workspaces'] });
|
||||
},
|
||||
onError: (error: AxiosError<CustomError>) => {
|
||||
console.error('워크스페이스 생성 실패:', error.message);
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(data);
|
||||
// createWorkspace.mutate(
|
||||
// { memberId, data },
|
||||
// {
|
||||
// onSuccess: () => {
|
||||
// console.log('워크스페이스가 성공적으로 생성되었습니다.');
|
||||
// queryClient.invalidateQueries({ queryKey: ['workspaces'] });
|
||||
// },
|
||||
// onError: (error: AxiosError<CustomError>) => {
|
||||
// console.error('워크스페이스 생성 실패:', error.message);
|
||||
// },
|
||||
// }
|
||||
// );
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
@ -62,7 +60,7 @@ export default function WorkspaceBrowseLayout() {
|
||||
<WorkSpaceCreateModal onSubmit={handleCreateWorkspace} />
|
||||
</div>
|
||||
{workspaces.length > 0 ? (
|
||||
workspaces.map((workspace: WorkspaceResponseDTO) => (
|
||||
workspaces.map((workspace: WorkspaceResponse) => (
|
||||
<NavLink
|
||||
to={`/browse/${workspace.id}`}
|
||||
key={workspace.id}
|
||||
@ -76,11 +74,11 @@ export default function WorkspaceBrowseLayout() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-[calc(100%-280px)] flex-col gap-24">
|
||||
<Suspense fallback={<div></div>}>
|
||||
<main className="grow">
|
||||
<main className="grow">
|
||||
<Suspense fallback={<div></div>}>
|
||||
<Outlet />
|
||||
</main>
|
||||
</Suspense>
|
||||
</Suspense>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, Outlet } from 'react-router-dom';
|
||||
import Header from '../Header';
|
||||
import { Label, Project, DirectoryItem, FileItem } from '@/types';
|
||||
import { Label, Project } from '@/types';
|
||||
import { ResizablePanelGroup, ResizablePanel } from '../ui/resizable';
|
||||
import WorkspaceSidebar from '../WorkspaceSidebar';
|
||||
import WorkspaceLabelBar from '../WorkspaceLabelBar';
|
||||
import { fetchFolderApi } from '@/api/folderApi';
|
||||
import { getWorkspaceApi } from '@/api/workspaceApi';
|
||||
import { getAllProjectsApi } from '@/api/projectApi';
|
||||
import useAuthStore from '@/stores/useAuthStore';
|
||||
import useCanvasStore from '@/stores/useCanvasStore';
|
||||
import useFolderQuery from '@/queries/useFolderQuery';
|
||||
import useWorkspaceQuery from '@/queries/useWorkspaceQuery';
|
||||
import useProjectListQuery from '@/queries/useProjectListQuery';
|
||||
|
||||
const mockLabels: Label[] = [
|
||||
{
|
||||
@ -50,103 +50,136 @@ const mockLabels: Label[] = [
|
||||
|
||||
export default function WorkspaceLayout() {
|
||||
const setLabels = useCanvasStore((state) => state.setLabels);
|
||||
const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();
|
||||
const params = useParams<{ workspaceId: string; projectId: string }>();
|
||||
const workspaceId = Number(params.workspaceId);
|
||||
const projectId = Number(params.projectId);
|
||||
const [workspace, setWorkspace] = useState<{ name: string; projects: Project[] }>({
|
||||
name: '',
|
||||
projects: [],
|
||||
});
|
||||
const profile = useAuthStore((state) => state.profile);
|
||||
const memberId = profile?.id || 0;
|
||||
const { data: workspaceData } = useWorkspaceQuery(workspaceId, memberId);
|
||||
const { data: projectListData } = useProjectListQuery(workspaceId, memberId);
|
||||
const { data: folderData } = useFolderQuery(projectId, 0, memberId);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchWorkspaceData = async (workspaceId: number, memberId: number) => {
|
||||
try {
|
||||
const workspaceResponse = await getWorkspaceApi(workspaceId, memberId);
|
||||
if (workspaceResponse.isSuccess) {
|
||||
const workspaceTitle = workspaceResponse.data.title;
|
||||
setWorkspace((prev) => ({
|
||||
...prev,
|
||||
name: workspaceTitle,
|
||||
}));
|
||||
fetchProjects(workspaceId, memberId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('워크스페이스 조회 실패:', error);
|
||||
}
|
||||
};
|
||||
if (!workspaceData) return;
|
||||
setWorkspace((prev) => ({
|
||||
...prev,
|
||||
name: workspaceData.title,
|
||||
}));
|
||||
}, [workspaceData]);
|
||||
|
||||
const fetchProjects = async (workspaceId: number, memberId: number) => {
|
||||
try {
|
||||
const projectResponse = await getAllProjectsApi(workspaceId, memberId);
|
||||
if (projectResponse.isSuccess) {
|
||||
const projects = await Promise.all(
|
||||
projectResponse.data.workspaceResponses.map(async (project) => {
|
||||
const children = await fetchFolderWithImages(project.id, memberId);
|
||||
return {
|
||||
id: project.id,
|
||||
name: project.title,
|
||||
type: capitalizeType(project.projectType),
|
||||
children,
|
||||
};
|
||||
})
|
||||
);
|
||||
setWorkspace((prev) => ({
|
||||
...prev,
|
||||
projects: projects as Project[],
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('프로젝트 목록 조회 실패:', error);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!projectListData) return;
|
||||
console.log(folderData);
|
||||
const projects = projectListData.workspaceResponses.map(
|
||||
(project): Project => ({
|
||||
id: project.id,
|
||||
name: project.title,
|
||||
type: (project.projectType.charAt(0).toUpperCase() + project.projectType.slice(1)) as
|
||||
| 'Classification'
|
||||
| 'Detection'
|
||||
| 'Segmentation',
|
||||
children: [],
|
||||
})
|
||||
);
|
||||
setWorkspace((prev) => ({
|
||||
...prev,
|
||||
projects,
|
||||
}));
|
||||
}, [projectListData]);
|
||||
|
||||
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',
|
||||
status: image.status === 'COMPLETED' ? 'done' : 'idle',
|
||||
}));
|
||||
// useEffect(() => {
|
||||
// const fetchWorkspaceData = async (workspaceId: number, memberId: number) => {
|
||||
// try {
|
||||
// const workspaceResponse = await getWorkspaceApi(workspaceId, memberId);
|
||||
// if (workspaceResponse.isSuccess) {
|
||||
// const workspaceTitle = workspaceResponse.data.title;
|
||||
// setWorkspace((prev) => ({
|
||||
// ...prev,
|
||||
// name: workspaceTitle,
|
||||
// }));
|
||||
// fetchProjects(workspaceId, memberId);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error('워크스페이스 조회 실패:', error);
|
||||
// }
|
||||
// };
|
||||
|
||||
return [
|
||||
{
|
||||
id: folderResponse.data.id,
|
||||
name: folderResponse.data.title,
|
||||
type: 'directory',
|
||||
children: files,
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('폴더 및 이미지 조회 실패:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
// const fetchProjects = async (workspaceId: number, memberId: number) => {
|
||||
// try {
|
||||
// const projectResponse = await getAllProjectsApi(workspaceId, memberId);
|
||||
// if (projectResponse.isSuccess) {
|
||||
// const projects = await Promise.all(
|
||||
// projectResponse.data.workspaceResponses.map(async (project) => {
|
||||
// const children = await fetchFolderWithImages(project.id, memberId);
|
||||
// return {
|
||||
// id: project.id,
|
||||
// name: project.title,
|
||||
// type: capitalizeType(project.projectType),
|
||||
// children,
|
||||
// };
|
||||
// })
|
||||
// );
|
||||
// setWorkspace((prev) => ({
|
||||
// ...prev,
|
||||
// projects: projects as Project[],
|
||||
// }));
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error('프로젝트 목록 조회 실패:', error);
|
||||
// }
|
||||
// };
|
||||
|
||||
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}`);
|
||||
}
|
||||
};
|
||||
// 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',
|
||||
// status: image.status === 'COMPLETED' ? 'done' : 'idle',
|
||||
// }));
|
||||
|
||||
if (workspaceId && memberId) {
|
||||
fetchWorkspaceData(Number(workspaceId), memberId);
|
||||
}
|
||||
}, [workspaceId, projectId, memberId]);
|
||||
// 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]);
|
||||
|
||||
useEffect(() => {
|
||||
setLabels(mockLabels);
|
||||
|
@ -1,114 +1,114 @@
|
||||
import { useQuery, UseQueryResult, useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
import {
|
||||
getProjectApi,
|
||||
updateProjectApi,
|
||||
deleteProjectApi,
|
||||
getAllProjectsApi,
|
||||
createProjectApi,
|
||||
addProjectMemberApi,
|
||||
removeProjectMemberApi,
|
||||
} from '@/api/projectApi';
|
||||
import { BaseResponse, ProjectResponseDTO, ProjectListResponseDTO, CustomError } from '@/types';
|
||||
// TODO: 훅 재설계
|
||||
// import { useQuery, UseQueryResult, useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query';
|
||||
// import { AxiosError } from 'axios';
|
||||
// import {
|
||||
// getProject,
|
||||
// updateProject,
|
||||
// deleteProject,
|
||||
// getAllProjects,
|
||||
// createProject,
|
||||
// addProjectMember,
|
||||
// removeProjectMember,
|
||||
// } from '@/api/projectApi';
|
||||
// import { BaseResponse, ProjectResponse, ProjectListResponse, CustomError } from '@/types';
|
||||
|
||||
export const useGetProject = (
|
||||
projectId: number,
|
||||
memberId: number
|
||||
): UseQueryResult<BaseResponse<ProjectResponseDTO>, AxiosError<CustomError>> => {
|
||||
return useQuery<BaseResponse<ProjectResponseDTO>, AxiosError<CustomError>>({
|
||||
queryKey: ['project', projectId],
|
||||
queryFn: () => getProjectApi(projectId, memberId),
|
||||
});
|
||||
};
|
||||
// export const useGetProject = (
|
||||
// projectId: number,
|
||||
// memberId: number
|
||||
// ): UseQueryResult<BaseResponse<ProjectResponse>, AxiosError<CustomError>> => {
|
||||
// return useQuery<BaseResponse<ProjectResponse>, AxiosError<CustomError>>({
|
||||
// queryKey: ['project', projectId],
|
||||
// queryFn: () => getProject(projectId, memberId),
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useUpdateProject = (): UseMutationResult<
|
||||
BaseResponse<ProjectResponseDTO>,
|
||||
AxiosError<CustomError>,
|
||||
{
|
||||
projectId: number;
|
||||
memberId: number;
|
||||
data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' };
|
||||
}
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, memberId, data }) => updateProjectApi(projectId, memberId, data),
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['project', data.data.id] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useUpdateProject = (): UseMutationResult<
|
||||
// BaseResponse<ProjectResponse>,
|
||||
// AxiosError<CustomError>,
|
||||
// {
|
||||
// projectId: number;
|
||||
// memberId: number;
|
||||
// data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' };
|
||||
// }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ projectId, memberId, data }) => updateProject(projectId, memberId, data),
|
||||
// onSuccess: (data) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['project', data.data.id] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useDeleteProject = (): UseMutationResult<
|
||||
BaseResponse<null>,
|
||||
AxiosError<CustomError>,
|
||||
{ projectId: number; memberId: number }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, memberId }) => deleteProjectApi(projectId, memberId),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useDeleteProject = (): UseMutationResult<
|
||||
// BaseResponse<null>,
|
||||
// AxiosError<CustomError>,
|
||||
// { projectId: number; memberId: number }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ projectId, memberId }) => deleteProject(projectId, memberId),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useGetAllProjects = (
|
||||
workspaceId: number,
|
||||
memberId: number,
|
||||
options?: { enabled: boolean }
|
||||
): UseQueryResult<BaseResponse<ProjectListResponseDTO>, AxiosError<CustomError>> => {
|
||||
return useQuery<BaseResponse<ProjectListResponseDTO>, AxiosError<CustomError>>({
|
||||
queryKey: ['projects', workspaceId],
|
||||
queryFn: () => getAllProjectsApi(workspaceId, memberId),
|
||||
enabled: options?.enabled,
|
||||
});
|
||||
};
|
||||
// export const useGetAllProjects = (
|
||||
// workspaceId: number,
|
||||
// memberId: number,
|
||||
// options?: { enabled: boolean }
|
||||
// ): UseQueryResult<BaseResponse<ProjectListResponse>, AxiosError<CustomError>> => {
|
||||
// return useQuery<BaseResponse<ProjectListResponse>, AxiosError<CustomError>>({
|
||||
// queryKey: ['projects', workspaceId],
|
||||
// queryFn: () => getAllProjects(workspaceId, memberId),
|
||||
// enabled: options?.enabled,
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useCreateProject = (): UseMutationResult<
|
||||
BaseResponse<ProjectResponseDTO>,
|
||||
AxiosError<CustomError>,
|
||||
{
|
||||
workspaceId: number;
|
||||
memberId: number;
|
||||
data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' };
|
||||
}
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ workspaceId, memberId, data }) => createProjectApi(workspaceId, memberId, data),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['projects', variables.workspaceId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useCreateProject = (): UseMutationResult<
|
||||
// BaseResponse<ProjectResponse>,
|
||||
// AxiosError<CustomError>,
|
||||
// {
|
||||
// workspaceId: number;
|
||||
// memberId: number;
|
||||
// data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' };
|
||||
// }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ workspaceId, memberId, data }) => createProject(workspaceId, memberId, data),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['projects', variables.workspaceId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useAddProjectMember = (): UseMutationResult<
|
||||
BaseResponse<null>,
|
||||
AxiosError<CustomError>,
|
||||
{ projectId: number; memberId: number; newMemberId: number; privilegeType: string }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, memberId, newMemberId, privilegeType }) =>
|
||||
addProjectMemberApi(projectId, memberId, newMemberId, privilegeType),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useAddProjectMember = (): UseMutationResult<
|
||||
// BaseResponse<null>,
|
||||
// AxiosError<CustomError>,
|
||||
// { projectId: number; memberId: number; newMemberId: number; privilegeType: string }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ projectId, memberId, newMemberId, privilegeType }) =>
|
||||
// addProjectMember(projectId, memberId, newMemberId, privilegeType),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useRemoveProjectMember = (): UseMutationResult<
|
||||
BaseResponse<null>,
|
||||
AxiosError<CustomError>,
|
||||
{ projectId: number; memberId: number; targetMemberId: number }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ projectId, memberId, targetMemberId }) =>
|
||||
removeProjectMemberApi(projectId, memberId, targetMemberId),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useRemoveProjectMember = (): UseMutationResult<
|
||||
// BaseResponse<null>,
|
||||
// AxiosError<CustomError>,
|
||||
// { projectId: number; memberId: number; targetMemberId: number }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ projectId, memberId, targetMemberId }) => removeProjectMember(projectId, memberId, targetMemberId),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['project', variables.projectId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
@ -1,104 +1,89 @@
|
||||
import { useQuery, UseQueryResult, useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
import {
|
||||
getWorkspaceApi,
|
||||
updateWorkspaceApi,
|
||||
deleteWorkspaceApi,
|
||||
getAllWorkspacesApi,
|
||||
createWorkspaceApi,
|
||||
addWorkspaceMemberApi,
|
||||
removeWorkspaceMemberApi,
|
||||
} from '@/api/workspaceApi';
|
||||
import { BaseResponse, WorkspaceResponseDTO, WorkspaceListResponseDTO, CustomError } from '@/types';
|
||||
// TODO: 훅 재설계
|
||||
// import { useQuery } from '@tanstack/react-query';
|
||||
// import { getWorkspace, getWorkspaceList } from '@/api/workspaceApi';
|
||||
|
||||
export const useGetWorkspace = (
|
||||
workspaceId: number,
|
||||
memberId: number
|
||||
): UseQueryResult<BaseResponse<WorkspaceResponseDTO>, AxiosError<CustomError>> => {
|
||||
return useQuery<BaseResponse<WorkspaceResponseDTO>, AxiosError<CustomError>>({
|
||||
queryKey: ['workspace', workspaceId],
|
||||
queryFn: () => getWorkspaceApi(workspaceId, memberId),
|
||||
});
|
||||
};
|
||||
// export const useGetWorkspace = (workspaceId: number, memberId: number) => {
|
||||
// return useQuery({
|
||||
// queryKey: ['workspace', workspaceId],
|
||||
// queryFn: () => getWorkspace(workspaceId, memberId),
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useUpdateWorkspace = (): UseMutationResult<
|
||||
BaseResponse<WorkspaceResponseDTO>,
|
||||
AxiosError<CustomError>,
|
||||
{ workspaceId: number; memberId: number; data: { title: string; content: string } }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ workspaceId, memberId, data }) => updateWorkspaceApi(workspaceId, memberId, data),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useGetWorkspaceList = (memberId: number, lastWorkspaceId?: number, limit?: number) => {
|
||||
// return useQuery({
|
||||
// queryKey: ['workspaces'],
|
||||
// queryFn: () => getWorkspaceList(memberId, lastWorkspaceId, limit),
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useDeleteWorkspace = (): UseMutationResult<
|
||||
BaseResponse<null>,
|
||||
AxiosError<CustomError>,
|
||||
{ workspaceId: number; memberId: number }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ workspaceId, memberId }) => deleteWorkspaceApi(workspaceId, memberId),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// TODO: 수정된 쿼리에 맞게 훅 수정
|
||||
// export const useUpdateWorkspace = (): UseMutationResult<
|
||||
// BaseResponse<WorkspaceResponse>,
|
||||
// AxiosError<CustomError>,
|
||||
// { workspaceId: number; memberId: number; data: { title: string; content: string } }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ workspaceId, memberId, data }) => updateWorkspace(workspaceId, memberId, data),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useGetAllWorkspaces = (
|
||||
memberId: number,
|
||||
lastWorkspaceId?: number,
|
||||
limit?: number
|
||||
): UseQueryResult<BaseResponse<WorkspaceListResponseDTO>, AxiosError<CustomError>> => {
|
||||
return useQuery<BaseResponse<WorkspaceListResponseDTO>, AxiosError<CustomError>>({
|
||||
queryKey: ['workspaces'],
|
||||
queryFn: () => getAllWorkspacesApi(memberId, lastWorkspaceId, limit),
|
||||
});
|
||||
};
|
||||
// export const useDeleteWorkspace = (): UseMutationResult<
|
||||
// BaseResponse<null>,
|
||||
// AxiosError<CustomError>,
|
||||
// { workspaceId: number; memberId: number }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ workspaceId, memberId }) => deleteWorkspace(workspaceId, memberId),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useCreateWorkspace = (): UseMutationResult<
|
||||
BaseResponse<WorkspaceResponseDTO>,
|
||||
AxiosError<CustomError>,
|
||||
{ memberId: number; data: { title: string; content: string } }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ memberId, data }) => createWorkspaceApi(memberId, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['workspaces'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useCreateWorkspace = (): UseMutationResult<
|
||||
// BaseResponse<WorkspaceResponse>,
|
||||
// AxiosError<CustomError>,
|
||||
// { memberId: number; data: { title: string; content: string } }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ memberId, data }) => createWorkspace(memberId, data),
|
||||
// onSuccess: () => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['workspaces'] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useAddWorkspaceMember = (): UseMutationResult<
|
||||
BaseResponse<null>,
|
||||
AxiosError<CustomError>,
|
||||
{ workspaceId: number; memberId: number; newMemberId: number }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ workspaceId, memberId, newMemberId }) => addWorkspaceMemberApi(workspaceId, memberId, newMemberId),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useAddWorkspaceMember = (): UseMutationResult<
|
||||
// BaseResponse<null>,
|
||||
// AxiosError<CustomError>,
|
||||
// { workspaceId: number; memberId: number; newMemberId: number }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ workspaceId, memberId, newMemberId }) => addWorkspaceMember(workspaceId, memberId, newMemberId),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useRemoveWorkspaceMember = (): UseMutationResult<
|
||||
BaseResponse<null>,
|
||||
AxiosError<CustomError>,
|
||||
{ workspaceId: number; memberId: number; targetMemberId: number }
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ workspaceId, memberId, targetMemberId }) =>
|
||||
removeWorkspaceMemberApi(workspaceId, memberId, targetMemberId),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
// export const useRemoveWorkspaceMember = (): UseMutationResult<
|
||||
// BaseResponse<null>,
|
||||
// AxiosError<CustomError>,
|
||||
// { workspaceId: number; memberId: number; targetMemberId: number }
|
||||
// > => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: ({ workspaceId, memberId, targetMemberId }) =>
|
||||
// removeWorkspaceMember(workspaceId, memberId, targetMemberId),
|
||||
// onSuccess: (_, variables) => {
|
||||
// queryClient.invalidateQueries({ queryKey: ['workspace', variables.workspaceId] });
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
@ -1,21 +1,21 @@
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import {
|
||||
BaseResponse,
|
||||
ProjectResponseDTO,
|
||||
FolderResponseDTO,
|
||||
ImageResponseDTO,
|
||||
WorkspaceResponseDTO,
|
||||
MemberResponseDTO,
|
||||
RefreshTokenResponseDTO,
|
||||
AutoLabelingResponseDTO,
|
||||
ProjectListResponseDTO,
|
||||
ProjectResponse,
|
||||
FolderResponse,
|
||||
ImageResponse,
|
||||
WorkspaceResponse,
|
||||
MemberResponse,
|
||||
RefreshTokenResponse,
|
||||
AutoLabelingResponse,
|
||||
ProjectListResponse,
|
||||
} from '@/types';
|
||||
|
||||
export const handlers = [
|
||||
// Auth Handlers
|
||||
http.post('/api/auth/reissue', () => {
|
||||
// 토큰 재발급 핸들러
|
||||
const response: BaseResponse<RefreshTokenResponseDTO> = {
|
||||
const response: BaseResponse<RefreshTokenResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '토큰 재발급 성공',
|
||||
@ -28,7 +28,7 @@ export const handlers = [
|
||||
|
||||
http.get('/api/auth/profile', () => {
|
||||
// 사용자 프로필 핸들러
|
||||
const response: BaseResponse<MemberResponseDTO> = {
|
||||
const response: BaseResponse<MemberResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '사용자 정보 가져오기 성공',
|
||||
@ -47,7 +47,7 @@ export const handlers = [
|
||||
http.get('/api/workspaces/:workspaceId', ({ params }) => {
|
||||
// 워크스페이스 조회 핸들러
|
||||
const { workspaceId } = params;
|
||||
const response: BaseResponse<WorkspaceResponseDTO> = {
|
||||
const response: BaseResponse<WorkspaceResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '워크스페이스 조회 성공',
|
||||
@ -68,7 +68,7 @@ export const handlers = [
|
||||
http.put('/api/workspaces/:workspaceId', ({ params }) => {
|
||||
// 워크스페이스 수정 핸들러
|
||||
const { workspaceId } = params;
|
||||
const response: BaseResponse<WorkspaceResponseDTO> = {
|
||||
const response: BaseResponse<WorkspaceResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '워크스페이스 수정 성공',
|
||||
@ -102,7 +102,7 @@ export const handlers = [
|
||||
|
||||
http.get('/api/workspaces', () => {
|
||||
// 워크스페이스 목록 조회 핸들러
|
||||
const response: BaseResponse<{ workspaceResponses: WorkspaceResponseDTO[] }> = {
|
||||
const response: BaseResponse<{ workspaceResponses: WorkspaceResponse[] }> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '워크스페이스 목록 조회 성공',
|
||||
@ -128,7 +128,7 @@ export const handlers = [
|
||||
http.get('/api/projects/:projectId', ({ params }) => {
|
||||
// 프로젝트 조회 핸들러
|
||||
const { projectId } = params;
|
||||
const response: BaseResponse<ProjectResponseDTO> = {
|
||||
const response: BaseResponse<ProjectResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '프로젝트 조회 성공',
|
||||
@ -148,7 +148,7 @@ export const handlers = [
|
||||
|
||||
http.post('/api/workspaces/:workspaceId/projects', () => {
|
||||
// 프로젝트 생성 핸들러
|
||||
const response: BaseResponse<ProjectResponseDTO> = {
|
||||
const response: BaseResponse<ProjectResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '프로젝트 생성 성공',
|
||||
@ -169,7 +169,7 @@ export const handlers = [
|
||||
http.put('/api/projects/:projectId', ({ params }) => {
|
||||
// 프로젝트 수정 핸들러
|
||||
const { projectId } = params;
|
||||
const response: BaseResponse<ProjectResponseDTO> = {
|
||||
const response: BaseResponse<ProjectResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '프로젝트 수정 성공',
|
||||
@ -204,7 +204,7 @@ export const handlers = [
|
||||
|
||||
http.get('/api/projects/:projectId/folders/:folderId', ({ params }) => {
|
||||
const { folderId } = params;
|
||||
const response: BaseResponse<FolderResponseDTO> = {
|
||||
const response: BaseResponse<FolderResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '폴더 조회 성공',
|
||||
@ -241,7 +241,7 @@ export const handlers = [
|
||||
field: 'id',
|
||||
code: '1001',
|
||||
message: 'ID format issue',
|
||||
objectName: 'FolderResponseDTO',
|
||||
objectName: 'FolderResponse',
|
||||
},
|
||||
],
|
||||
isSuccess: true,
|
||||
@ -261,7 +261,7 @@ export const handlers = [
|
||||
const limit = parseInt(url.searchParams.get('limit') || '10', 10);
|
||||
|
||||
// 프로젝트 데이터 예시 생성
|
||||
const projects: ProjectResponseDTO[] = Array.from({ length: limit }, (_, index) => ({
|
||||
const projects: ProjectResponse[] = Array.from({ length: limit }, (_, index) => ({
|
||||
id: lastProjectId + index + 1,
|
||||
title: `프로젝트 ${lastProjectId + index + 1}`,
|
||||
workspaceId,
|
||||
@ -274,7 +274,7 @@ export const handlers = [
|
||||
}));
|
||||
|
||||
// 응답 생성
|
||||
const response: BaseResponse<ProjectListResponseDTO> = {
|
||||
const response: BaseResponse<ProjectListResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '프로젝트 목록 조회 성공',
|
||||
@ -290,7 +290,7 @@ export const handlers = [
|
||||
|
||||
http.post('/api/projects/:projectId/folders', () => {
|
||||
// 폴더 생성 핸들러
|
||||
const response: BaseResponse<FolderResponseDTO> = {
|
||||
const response: BaseResponse<FolderResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '폴더 생성 성공',
|
||||
@ -310,7 +310,7 @@ export const handlers = [
|
||||
http.get('/api/projects/:projectId/folders/:folderId/images/:imageId', ({ params }) => {
|
||||
// 이미지 조회 핸들러
|
||||
const { imageId } = params;
|
||||
const response: BaseResponse<ImageResponseDTO> = {
|
||||
const response: BaseResponse<ImageResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '이미지 조회 성공',
|
||||
@ -434,8 +434,8 @@ export const handlers = [
|
||||
// "imageDepth": 4
|
||||
// }`;
|
||||
|
||||
// AutoLabelingResponseDTO 예시
|
||||
const response: BaseResponse<AutoLabelingResponseDTO> = {
|
||||
// AutoLabelingResponse 예시
|
||||
const response: BaseResponse<AutoLabelingResponse> = {
|
||||
status: 200,
|
||||
code: 0,
|
||||
message: '프로젝트 오토 레이블링 성공',
|
||||
|
@ -1,45 +1,27 @@
|
||||
import { useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import GoogleLogo from '@/assets/icons/web_neutral_rd_ctn@1x.png';
|
||||
import useAuthStore from '@/stores/useAuthStore';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { fetchProfileApi, reissueTokenApi } from '@/api/authApi';
|
||||
import { SuccessResponse, MemberResponseDTO, CustomError } from '@/types';
|
||||
import { AxiosError } from 'axios';
|
||||
import { getProfile, reissueToken } from '@/api/authApi';
|
||||
|
||||
const BASE_URL = import.meta.env.VITE_API_URL;
|
||||
|
||||
export default function Home() {
|
||||
const navigate = useNavigate();
|
||||
const { isLoggedIn, accessToken, setLoggedIn, profile, setProfile } = useAuthStore();
|
||||
const hasFetchedProfile = useRef(false);
|
||||
|
||||
if (!isLoggedIn && !profile && !hasFetchedProfile.current && accessToken) {
|
||||
setLoggedIn(true, accessToken);
|
||||
fetchProfileApi()
|
||||
.then((data: SuccessResponse<MemberResponseDTO>) => {
|
||||
if (data?.isSuccess && data.data) {
|
||||
setProfile(data.data);
|
||||
hasFetchedProfile.current = true;
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError<CustomError>) => {
|
||||
alert('프로필을 가져오는 중 오류가 발생했습니다. 다시 시도해주세요.');
|
||||
console.error('프로필 가져오기 실패:', error?.response?.data?.message || '알 수 없는 오류');
|
||||
});
|
||||
getProfile().then((data) => {
|
||||
setProfile(data);
|
||||
hasFetchedProfile.current = true;
|
||||
});
|
||||
}
|
||||
|
||||
const handleGoogleSignIn = () => {
|
||||
window.location.href = `${BASE_URL}/api/login/oauth2/authorization/google`;
|
||||
};
|
||||
|
||||
const handleStart = () => {
|
||||
navigate('/browse');
|
||||
};
|
||||
|
||||
const handleReissueToken = async () => {
|
||||
try {
|
||||
const response = await reissueTokenApi();
|
||||
const response = await reissueToken();
|
||||
console.log('토큰 재발급 성공:', response);
|
||||
alert('토큰 재발급 성공! 새로운 액세스 토큰을 콘솔에서 확인하세요.');
|
||||
} catch (error) {
|
||||
@ -72,8 +54,9 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
{!isLoggedIn ? (
|
||||
<button
|
||||
onClick={handleGoogleSignIn}
|
||||
<Link
|
||||
to={`${BASE_URL}/login/oauth2/authorization/google`}
|
||||
// onClick={handleGoogleSignIn}
|
||||
className="mb-4 transition hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-gray-300 active:opacity-80"
|
||||
>
|
||||
<img
|
||||
@ -81,15 +64,15 @@ export default function Home() {
|
||||
alt="Sign in with Google"
|
||||
className="h-auto w-full"
|
||||
/>
|
||||
</button>
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
asChild
|
||||
variant="outlinePrimary"
|
||||
size="lg"
|
||||
onClick={handleStart}
|
||||
>
|
||||
시작하기
|
||||
<Link to="/browse">시작하기</Link>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlinePrimary"
|
@ -1,52 +1,48 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import ProjectCard from '@/components/ProjectCard';
|
||||
import { Smile } from 'lucide-react';
|
||||
import ProjectCreateModal from '../ProjectCreateModal';
|
||||
import { useGetAllProjects, useCreateProject } from '@/hooks/useProjectHooks';
|
||||
import ProjectCreateModal from '../components/ProjectCreateModal';
|
||||
import useAuthStore from '@/stores/useAuthStore';
|
||||
import { ProjectResponseDTO } from '@/types';
|
||||
import { ProjectResponse } from '@/types';
|
||||
import useProjectListQuery from '@/queries/useProjectListQuery';
|
||||
import { webPath } from '@/router';
|
||||
|
||||
export default function WorkspaceBrowseDetail() {
|
||||
const { workspaceId } = useParams<{ workspaceId: string }>();
|
||||
const numericWorkspaceId = Number(workspaceId);
|
||||
const params = useParams<{ workspaceId: string }>();
|
||||
const workspaceId = Number(params.workspaceId);
|
||||
const { profile } = useAuthStore();
|
||||
const memberId = profile?.id ?? 0;
|
||||
const navigate = useNavigate();
|
||||
// const createProject = useCreateProject();
|
||||
|
||||
const {
|
||||
data: projectsResponse,
|
||||
isLoading,
|
||||
isError,
|
||||
refetch,
|
||||
} = useGetAllProjects(numericWorkspaceId, memberId, {
|
||||
enabled: !isNaN(numericWorkspaceId),
|
||||
});
|
||||
|
||||
const createProject = useCreateProject();
|
||||
const { data: projectsResponse, isError } = useProjectListQuery(workspaceId, memberId);
|
||||
|
||||
const handleCreateProject = (data: { title: string; labelType: 'classification' | 'detection' | 'segmentation' }) => {
|
||||
createProject.mutate(
|
||||
{
|
||||
workspaceId: numericWorkspaceId,
|
||||
memberId,
|
||||
data: { title: data.title, projectType: data.labelType },
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
console.log('프로젝트가 성공적으로 생성되었습니다.');
|
||||
refetch();
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('프로젝트 생성 실패:', error);
|
||||
const errorMessage = error?.response?.data?.message || error.message || '알 수 없는 오류';
|
||||
console.error('프로젝트 생성 실패:', errorMessage);
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(data);
|
||||
// createProject.mutate(
|
||||
// {
|
||||
// workspaceId: workspaceId,
|
||||
// memberId,
|
||||
// data: { title: data.title, projectType: data.labelType },
|
||||
// },
|
||||
// {
|
||||
// onSuccess: () => {
|
||||
// console.log('프로젝트가 성공적으로 생성되었습니다.');
|
||||
// refetch();
|
||||
// },
|
||||
// onError: (error) => {
|
||||
// console.error('프로젝트 생성 실패:', error);
|
||||
// const errorMessage = error?.response?.data?.message || error.message || '알 수 없는 오류';
|
||||
// console.error('프로젝트 생성 실패:', errorMessage);
|
||||
// },
|
||||
// }
|
||||
// );
|
||||
};
|
||||
|
||||
const projects: ProjectResponseDTO[] = projectsResponse?.data?.workspaceResponses ?? [];
|
||||
// TODO: 반환 형식 반영 projectsResponse?.workspaceResponses => projectResponse
|
||||
const projects: ProjectResponse[] = projectsResponse?.workspaceResponses ?? [];
|
||||
|
||||
if (isNaN(numericWorkspaceId)) {
|
||||
if (isNaN(workspaceId)) {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||
<div className="flex flex-col items-center">
|
||||
@ -60,10 +56,6 @@ export default function WorkspaceBrowseDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <p>Loading projects...</p>;
|
||||
}
|
||||
|
||||
if (isError || !workspaceId) {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||
@ -95,13 +87,13 @@ export default function WorkspaceBrowseDetail() {
|
||||
</div>
|
||||
{projects.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-6">
|
||||
{projects.map((project: ProjectResponseDTO) => (
|
||||
{projects.map((project: ProjectResponse) => (
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
title={project.title}
|
||||
description={project.projectType}
|
||||
onClick={() => {
|
||||
console.log('project id : ' + project.id);
|
||||
navigate(`${webPath.workspace()}/${workspaceId}/project/${project.id}`);
|
||||
}}
|
||||
/>
|
||||
))}
|
7
frontend/src/pages/WorkspaceBrowseIndex.tsx
Normal file
7
frontend/src/pages/WorkspaceBrowseIndex.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export default function WorkspaceBrowseIndex() {
|
||||
return (
|
||||
<div className="body-strong flex h-full w-full items-center justify-center text-gray-400">
|
||||
워크스페이스를 선택하세요
|
||||
</div>
|
||||
);
|
||||
}
|
9
frontend/src/queries/useProjectListQuery.ts
Normal file
9
frontend/src/queries/useProjectListQuery.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { getProjectList } from '@/api/projectApi';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
|
||||
export default function useProjectListQuery(workspaceId: number, memberId: number) {
|
||||
return useSuspenseQuery({
|
||||
queryKey: ['projects', workspaceId, memberId],
|
||||
queryFn: () => getProjectList(workspaceId, memberId),
|
||||
});
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { fetchWorkspaceList } from '@/api/workspaceApi';
|
||||
import { getWorkspaceList } from '@/api/workspaceApi';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
|
||||
export default function useWorkspaceListQuery(memberId: number, lastWorkspaceId?: number, limit?: number) {
|
||||
return useSuspenseQuery({
|
||||
queryKey: ['workspaceList'],
|
||||
queryFn: () => fetchWorkspaceList(memberId, lastWorkspaceId, limit),
|
||||
queryFn: () => getWorkspaceList(memberId, lastWorkspaceId, limit),
|
||||
});
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { fetchWorkspace } from '@/api/workspaceApi';
|
||||
import { getWorkspace } from '@/api/workspaceApi';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
|
||||
export default function useWorkspaceQuery(workspaceId: number, memberId: number) {
|
||||
return useSuspenseQuery({
|
||||
queryKey: ['workspace', workspaceId, memberId],
|
||||
queryFn: () => fetchWorkspace(workspaceId, memberId),
|
||||
queryFn: () => getWorkspace(workspaceId, memberId),
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import PageLayout from '@/components/PageLayout';
|
||||
import ImageCanvas from '@/components/ImageCanvas';
|
||||
import Home from '@/components/Home';
|
||||
import WorkspaceBrowseDetail from '@/components/WorkspaceBrowseDetail';
|
||||
import Home from '@/pages/Home';
|
||||
import WorkspaceBrowseDetail from '@/pages/WorkspaceBrowseDetail';
|
||||
import WorkspaceBrowseLayout from '@/components/WorkspaceBrowseLayout';
|
||||
import WorkspaceLayout from '@/components/WorkspaceLayout';
|
||||
import AdminLayout from '@/components/AdminLayout';
|
||||
@ -10,6 +10,8 @@ import AdminMemberManage from '@/components/AdminMemberManage';
|
||||
import OAuthCallback from '@/components/OAuthCallback';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { Suspense } from 'react';
|
||||
import WorkspaceBrowseIndex from '@/pages/WorkspaceBrowseIndex';
|
||||
|
||||
export const webPath = {
|
||||
home: () => '/',
|
||||
@ -33,12 +35,17 @@ const router = createBrowserRouter([
|
||||
],
|
||||
},
|
||||
{
|
||||
// FIXME: index에서 오류나지 않게 수정
|
||||
path: webPath.browse(),
|
||||
element: <WorkspaceBrowseLayout />,
|
||||
element: (
|
||||
<Suspense fallback={<div></div>}>
|
||||
<WorkspaceBrowseLayout />
|
||||
</Suspense>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <WorkspaceBrowseDetail />,
|
||||
element: <WorkspaceBrowseIndex />,
|
||||
},
|
||||
{
|
||||
path: ':workspaceId',
|
||||
@ -47,8 +54,13 @@ const router = createBrowserRouter([
|
||||
],
|
||||
},
|
||||
{
|
||||
// FIXME: index에서 오류나지 않게 수정
|
||||
path: `${webPath.workspace()}/:workspaceId`,
|
||||
element: <WorkspaceLayout />,
|
||||
element: (
|
||||
<Suspense fallback={<div></div>}>
|
||||
<WorkspaceLayout />
|
||||
</Suspense>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { MemberResponseDTO } from '@/types';
|
||||
import { MemberResponse } from '@/types';
|
||||
|
||||
interface AuthState {
|
||||
isLoggedIn: boolean;
|
||||
accessToken: string;
|
||||
profile: MemberResponseDTO | null;
|
||||
profile: MemberResponse | null;
|
||||
setLoggedIn: (status: boolean, token: string) => void;
|
||||
setProfile: (profile: MemberResponseDTO) => void;
|
||||
setProfile: (profile: MemberResponse) => void;
|
||||
clearAuth: () => void;
|
||||
}
|
||||
|
||||
@ -17,8 +17,8 @@ const useAuthStore = create<AuthState>()(
|
||||
isLoggedIn: false,
|
||||
accessToken: '',
|
||||
profile: null,
|
||||
setLoggedIn: (status, token) => set({ isLoggedIn: status, accessToken: token }),
|
||||
setProfile: (profile) => set({ profile }),
|
||||
setLoggedIn: (status: boolean, token: string) => set({ isLoggedIn: status, accessToken: token }),
|
||||
setProfile: (profile: MemberResponse) => set({ profile }),
|
||||
clearAuth: () => set({ isLoggedIn: false, accessToken: '', profile: null }),
|
||||
}),
|
||||
{
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
import { FolderResponseDTO } from '@/types';
|
||||
import { FolderResponse } from '@/types';
|
||||
|
||||
interface FolderState {
|
||||
folder: FolderResponseDTO | null;
|
||||
folder: FolderResponse | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
setFolder: (folder: FolderResponseDTO | null) => void;
|
||||
setFolder: (folder: FolderResponse | null) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
setError: (error: string | null) => void;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user