Merge branch 'fe/develop' of https://lab.ssafy.com/s11-s-project/S11P21S002 into fe/refactor/admin-model
This commit is contained in:
commit
aace66275f
@ -6,9 +6,9 @@ export async function reissueToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getProfile() {
|
export async function getProfile() {
|
||||||
return api
|
return api.get<MemberResponse>('/auth/profile').then(({ data }) => data);
|
||||||
.get<MemberResponse>('/auth/profile', {
|
}
|
||||||
withCredentials: true,
|
|
||||||
})
|
export async function logout() {
|
||||||
.then(({ data }) => data);
|
return api.post('/auth/logout').then(({ data }) => data);
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,8 @@ api.interceptors.response.use(
|
|||||||
return api
|
return api
|
||||||
.post<RefreshTokenResponse>(REFRESH_URL)
|
.post<RefreshTokenResponse>(REFRESH_URL)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
console.log(data);
|
|
||||||
const { accessToken } = data;
|
const { accessToken } = data;
|
||||||
useAuthStore.getState().setLoggedIn(true, accessToken);
|
useAuthStore.getState().setToken(accessToken);
|
||||||
if (error.config) {
|
if (error.config) {
|
||||||
return api(error.config);
|
return api(error.config);
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,6 @@ export async function saveImageLabels(
|
|||||||
return api.post(`/projects/${projectId}/images/${imageId}/label`, data).then(({ data }) => data);
|
return api.post(`/projects/${projectId}/images/${imageId}/label`, data).then(({ data }) => data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runAutoLabel(projectId: number, memberId: number) {
|
export async function runAutoLabel(projectId: number, modelId = 1) {
|
||||||
return api
|
return api.post(`/projects/${projectId}/auto`, { modelId }).then(({ data }) => data);
|
||||||
.post(
|
|
||||||
`/projects/${projectId}/label/auto`,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
params: { memberId },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(({ data }) => data);
|
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,9 @@ export default {
|
|||||||
component: CanvasControlBar,
|
component: CanvasControlBar,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Default = () => <CanvasControlBar saveJson={() => {}} />;
|
export const Default = () => (
|
||||||
|
<CanvasControlBar
|
||||||
|
saveJson={() => {}}
|
||||||
|
projectType="segmentation"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
@ -2,16 +2,22 @@ import useCanvasStore from '@/stores/useCanvasStore';
|
|||||||
import { LucideIcon, MousePointer2, PenTool, Save, 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({ saveJson }: { saveJson: () => void }) {
|
export default function CanvasControlBar({
|
||||||
|
saveJson,
|
||||||
|
projectType,
|
||||||
|
}: {
|
||||||
|
saveJson: () => void;
|
||||||
|
projectType: 'classification' | 'detection' | 'segmentation';
|
||||||
|
}) {
|
||||||
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 ';
|
||||||
const buttonClassName = 'hover:bg-gray-100';
|
const buttonClassName = 'hover:bg-gray-100';
|
||||||
const activeButtonClassName = 'bg-primary stroke-white';
|
const activeButtonClassName = 'bg-primary stroke-white';
|
||||||
|
|
||||||
const controls: { [key: string]: LucideIcon } = {
|
const controls: { [key: string]: LucideIcon } = {
|
||||||
pointer: MousePointer2,
|
pointer: MousePointer2,
|
||||||
rect: Square,
|
...(projectType === 'segmentation' ? { pen: PenTool } : { rect: Square }),
|
||||||
pen: PenTool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -31,7 +37,7 @@ export default function CanvasControlBar({ saveJson }: { saveJson: () => void })
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
<div className="h-5 w-0.5 rounded bg-gray-400" />
|
||||||
<button
|
<button
|
||||||
className={cn(buttonClassName, buttonBaseClassName)}
|
className={cn(buttonClassName, buttonBaseClassName)}
|
||||||
onClick={saveJson}
|
onClick={saveJson}
|
||||||
|
@ -20,7 +20,7 @@ export default function ImageCanvas() {
|
|||||||
const selectedLabelId = useCanvasStore((state) => state.selectedLabelId);
|
const selectedLabelId = useCanvasStore((state) => state.selectedLabelId);
|
||||||
const setSelectedLabelId = useCanvasStore((state) => state.setSelectedLabelId);
|
const setSelectedLabelId = useCanvasStore((state) => state.setSelectedLabelId);
|
||||||
const sidebarSize = useCanvasStore((state) => state.sidebarSize);
|
const sidebarSize = useCanvasStore((state) => state.sidebarSize);
|
||||||
const stageWidth = window.innerWidth * ((100 - sidebarSize) / 100) - 280;
|
const stageWidth = window.innerWidth * ((100 - sidebarSize) / 100) - 200;
|
||||||
const stageHeight = window.innerHeight - 64;
|
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);
|
||||||
@ -345,7 +345,10 @@ export default function ImageCanvas() {
|
|||||||
|
|
||||||
<Layer ref={dragLayerRef} />
|
<Layer ref={dragLayerRef} />
|
||||||
</Stage>
|
</Stage>
|
||||||
<CanvasControlBar saveJson={saveJson} />
|
<CanvasControlBar
|
||||||
|
saveJson={saveJson}
|
||||||
|
projectType={project.type}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div></div>
|
<div></div>
|
||||||
|
@ -8,15 +8,15 @@ import useWorkspaceListQuery from '@/queries/workspaces/useWorkspaceListQuery';
|
|||||||
import useCreateWorkspaceQuery from '@/queries/workspaces/useCreateWorkspaceQuery';
|
import useCreateWorkspaceQuery from '@/queries/workspaces/useCreateWorkspaceQuery';
|
||||||
|
|
||||||
export default function WorkspaceBrowseLayout() {
|
export default function WorkspaceBrowseLayout() {
|
||||||
const { profile, isLoggedIn } = useAuthStore();
|
const { profile } = useAuthStore();
|
||||||
const memberId = profile?.id ?? 0;
|
const memberId = profile?.id ?? 0;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoggedIn || memberId == 0) {
|
if (memberId == 0) {
|
||||||
navigate('/');
|
navigate('/');
|
||||||
}
|
}
|
||||||
}, [isLoggedIn, memberId, navigate]);
|
}, [memberId, navigate]);
|
||||||
|
|
||||||
const { data: workspacesResponse } = useWorkspaceListQuery(memberId ?? 0);
|
const { data: workspacesResponse } = useWorkspaceListQuery(memberId ?? 0);
|
||||||
const createWorkspace = useCreateWorkspaceQuery();
|
const createWorkspace = useCreateWorkspaceQuery();
|
||||||
|
@ -7,12 +7,14 @@ import useCanvasStore from '@/stores/useCanvasStore';
|
|||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import WorkspaceDropdownMenu from '../WorkspaceDropdownMenu';
|
import WorkspaceDropdownMenu from '../WorkspaceDropdownMenu';
|
||||||
|
import useAutoLabelQuery from '@/queries/projects/useAutoLabelQuery';
|
||||||
import useProjectStore from '@/stores/useProjectStore';
|
import useProjectStore from '@/stores/useProjectStore';
|
||||||
|
|
||||||
export default function ProjectStructure({ project }: { project: Project }) {
|
export default function ProjectStructure({ project }: { project: Project }) {
|
||||||
const setProject = useProjectStore((state) => state.setProject);
|
const setProject = useProjectStore((state) => state.setProject);
|
||||||
const image = useCanvasStore((state) => state.image);
|
const image = useCanvasStore((state) => state.image);
|
||||||
const { data: folderData, refetch } = useFolderQuery(project.id.toString(), 0);
|
const { data: folderData, refetch } = useFolderQuery(project.id.toString(), 0);
|
||||||
|
const requestAutoLabel = useAutoLabelQuery();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setProject(project);
|
setProject(project);
|
||||||
@ -60,7 +62,17 @@ export default function ProjectStructure({ project }: { project: Project }) {
|
|||||||
<Button
|
<Button
|
||||||
variant="outlinePrimary"
|
variant="outlinePrimary"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => console.log('autolabel')}
|
onClick={() => {
|
||||||
|
requestAutoLabel.mutate(
|
||||||
|
{ projectId: project.id },
|
||||||
|
{
|
||||||
|
onSuccess: refetch,
|
||||||
|
onError: () => {
|
||||||
|
alert('자동 레이블링을 요청하는 중 오류가 발생했습니다.');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Play
|
<Play
|
||||||
size={16}
|
size={16}
|
||||||
|
@ -4,11 +4,11 @@ import useProfileQuery from '@/queries/auth/useProfileQuery';
|
|||||||
export default function useHandleOAuthCallback() {
|
export default function useHandleOAuthCallback() {
|
||||||
const queryParams = new URLSearchParams(window.location.search);
|
const queryParams = new URLSearchParams(window.location.search);
|
||||||
const accessToken = queryParams.get('accessToken');
|
const accessToken = queryParams.get('accessToken');
|
||||||
const setLoggedIn = useAuthStore((state) => state.setLoggedIn);
|
const setToken = useAuthStore((state) => state.setToken);
|
||||||
const setProfile = useAuthStore((state) => state.setProfile);
|
const setProfile = useAuthStore((state) => state.setProfile);
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
setLoggedIn(true, accessToken);
|
setToken(accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: profile } = useProfileQuery();
|
const { data: profile } = useProfileQuery();
|
||||||
|
@ -1,25 +1,12 @@
|
|||||||
import { useRef } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import GoogleLogo from '@/assets/icons/web_neutral_rd_ctn@1x.png';
|
import GoogleLogo from '@/assets/icons/web_neutral_rd_ctn@1x.png';
|
||||||
import useAuthStore from '@/stores/useAuthStore';
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { getProfile } from '@/api/authApi';
|
|
||||||
const BASE_URL = import.meta.env.VITE_API_URL;
|
const BASE_URL = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { isLoggedIn, accessToken, setLoggedIn, profile, setProfile } = useAuthStore();
|
const { accessToken } = useAuthStore();
|
||||||
const hasFetchedProfile = useRef(false);
|
|
||||||
|
|
||||||
if (!isLoggedIn && !profile && !hasFetchedProfile.current && accessToken) {
|
|
||||||
setLoggedIn(true, accessToken);
|
|
||||||
getProfile().then((data) => {
|
|
||||||
setProfile(data);
|
|
||||||
hasFetchedProfile.current = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const handleGoogleSignIn = () => {
|
|
||||||
window.location.href = `${BASE_URL}/login/oauth2/authorization/google`;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-center bg-gray-50 p-8">
|
<div className="flex h-full flex-col items-center justify-center bg-gray-50 p-8">
|
||||||
@ -42,9 +29,10 @@ export default function Home() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isLoggedIn ? (
|
{!accessToken ? (
|
||||||
<button
|
<Link
|
||||||
onClick={handleGoogleSignIn}
|
to={`${BASE_URL}/login/oauth2/authorization/google`}
|
||||||
|
replace
|
||||||
className="mb-4 transition hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-gray-300 active:opacity-80"
|
className="mb-4 transition hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-gray-300 active:opacity-80"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -52,7 +40,7 @@ export default function Home() {
|
|||||||
alt="Sign in with Google"
|
alt="Sign in with Google"
|
||||||
className="h-auto w-full"
|
className="h-auto w-full"
|
||||||
/>
|
/>
|
||||||
</button> // 404 에러 방지
|
</Link> // 404 에러 방지
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
@ -4,12 +4,12 @@ import { reissueToken } from '@/api/authApi';
|
|||||||
|
|
||||||
export default function useReissueTokenQuery() {
|
export default function useReissueTokenQuery() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { setLoggedIn } = useAuthStore();
|
const { setToken } = useAuthStore();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: reissueToken,
|
mutationFn: reissueToken,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setLoggedIn(true, data.accessToken);
|
setToken(data.accessToken);
|
||||||
queryClient.invalidateQueries({ queryKey: ['profile'] });
|
queryClient.invalidateQueries({ queryKey: ['profile'] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
9
frontend/src/queries/projects/useAutoLabelQuery.ts
Normal file
9
frontend/src/queries/projects/useAutoLabelQuery.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { runAutoLabel } from '@/api/lablingApi';
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export default function useAutoLabelQuery() {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ projectId, modelId = 1 }: { projectId: number; modelId?: number }) =>
|
||||||
|
runAutoLabel(projectId, modelId),
|
||||||
|
});
|
||||||
|
}
|
@ -45,7 +45,6 @@ const router = createBrowserRouter([
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// FIXME: index에서 오류나지 않게 수정
|
|
||||||
path: webPath.browse(),
|
path: webPath.browse(),
|
||||||
element: (
|
element: (
|
||||||
<Suspense fallback={<PageLayout />}>
|
<Suspense fallback={<PageLayout />}>
|
||||||
@ -64,7 +63,6 @@ const router = createBrowserRouter([
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// FIXME: index에서 오류나지 않게 수정
|
|
||||||
path: `${webPath.workspace()}/:workspaceId`,
|
path: `${webPath.workspace()}/:workspaceId`,
|
||||||
element: (
|
element: (
|
||||||
<Suspense fallback={<div></div>}>
|
<Suspense fallback={<div></div>}>
|
||||||
|
@ -3,10 +3,9 @@ import { persist } from 'zustand/middleware';
|
|||||||
import { MemberResponse } from '@/types';
|
import { MemberResponse } from '@/types';
|
||||||
|
|
||||||
interface AuthState {
|
interface AuthState {
|
||||||
isLoggedIn: boolean;
|
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
profile: MemberResponse | null;
|
profile: MemberResponse | null;
|
||||||
setLoggedIn: (status: boolean, token: string) => void;
|
setToken: (token: string) => void;
|
||||||
setProfile: (profile: MemberResponse) => void;
|
setProfile: (profile: MemberResponse) => void;
|
||||||
clearAuth: () => void;
|
clearAuth: () => void;
|
||||||
}
|
}
|
||||||
@ -14,12 +13,11 @@ interface AuthState {
|
|||||||
const useAuthStore = create<AuthState>()(
|
const useAuthStore = create<AuthState>()(
|
||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
isLoggedIn: false,
|
|
||||||
accessToken: '',
|
accessToken: '',
|
||||||
profile: null,
|
profile: null,
|
||||||
setLoggedIn: (status: boolean, token: string) => set({ isLoggedIn: status, accessToken: token }),
|
setToken: (token: string) => set({ accessToken: token }),
|
||||||
setProfile: (profile: MemberResponse) => set({ profile }),
|
setProfile: (profile: MemberResponse) => set({ profile }),
|
||||||
clearAuth: () => set({ isLoggedIn: false, accessToken: '', profile: null }),
|
clearAuth: () => set({ accessToken: '', profile: null }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'auth-storage',
|
name: 'auth-storage',
|
||||||
|
Loading…
Reference in New Issue
Block a user