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

This commit is contained in:
정현조 2024-09-25 17:39:25 +09:00
commit aace66275f
14 changed files with 68 additions and 58 deletions

View File

@ -6,9 +6,9 @@ export async function reissueToken() {
}
export async function getProfile() {
return api
.get<MemberResponse>('/auth/profile', {
withCredentials: true,
})
.then(({ data }) => data);
return api.get<MemberResponse>('/auth/profile').then(({ data }) => data);
}
export async function logout() {
return api.post('/auth/logout').then(({ data }) => data);
}

View File

@ -27,9 +27,8 @@ api.interceptors.response.use(
return api
.post<RefreshTokenResponse>(REFRESH_URL)
.then(({ data }) => {
console.log(data);
const { accessToken } = data;
useAuthStore.getState().setLoggedIn(true, accessToken);
useAuthStore.getState().setToken(accessToken);
if (error.config) {
return api(error.config);
}

View File

@ -10,14 +10,6 @@ export async function saveImageLabels(
return api.post(`/projects/${projectId}/images/${imageId}/label`, data).then(({ data }) => data);
}
export async function runAutoLabel(projectId: number, memberId: number) {
return api
.post(
`/projects/${projectId}/label/auto`,
{},
{
params: { memberId },
}
)
.then(({ data }) => data);
export async function runAutoLabel(projectId: number, modelId = 1) {
return api.post(`/projects/${projectId}/auto`, { modelId }).then(({ data }) => data);
}

View File

@ -6,4 +6,9 @@ export default {
component: CanvasControlBar,
};
export const Default = () => <CanvasControlBar saveJson={() => {}} />;
export const Default = () => (
<CanvasControlBar
saveJson={() => {}}
projectType="segmentation"
/>
);

View File

@ -2,16 +2,22 @@ import useCanvasStore from '@/stores/useCanvasStore';
import { LucideIcon, MousePointer2, PenTool, Save, Square } from 'lucide-react';
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 setDrawState = useCanvasStore((state) => state.setDrawState);
const buttonBaseClassName = 'rounded-lg p-2 transition-colors ';
const buttonClassName = 'hover:bg-gray-100';
const activeButtonClassName = 'bg-primary stroke-white';
const controls: { [key: string]: LucideIcon } = {
pointer: MousePointer2,
rect: Square,
pen: PenTool,
...(projectType === 'segmentation' ? { pen: PenTool } : { rect: Square }),
};
return (
@ -31,7 +37,7 @@ export default function CanvasControlBar({ saveJson }: { saveJson: () => void })
</button>
);
})}
<div className="h-5 w-0.5 rounded bg-gray-400" />
<button
className={cn(buttonClassName, buttonBaseClassName)}
onClick={saveJson}

View File

@ -20,7 +20,7 @@ export default function ImageCanvas() {
const selectedLabelId = useCanvasStore((state) => state.selectedLabelId);
const setSelectedLabelId = useCanvasStore((state) => state.setSelectedLabelId);
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 stageRef = useRef<Konva.Stage>(null);
const dragLayerRef = useRef<Konva.Layer>(null);
@ -345,7 +345,10 @@ export default function ImageCanvas() {
<Layer ref={dragLayerRef} />
</Stage>
<CanvasControlBar saveJson={saveJson} />
<CanvasControlBar
saveJson={saveJson}
projectType={project.type}
/>
</div>
) : (
<div></div>

View File

@ -8,15 +8,15 @@ import useWorkspaceListQuery from '@/queries/workspaces/useWorkspaceListQuery';
import useCreateWorkspaceQuery from '@/queries/workspaces/useCreateWorkspaceQuery';
export default function WorkspaceBrowseLayout() {
const { profile, isLoggedIn } = useAuthStore();
const { profile } = useAuthStore();
const memberId = profile?.id ?? 0;
const navigate = useNavigate();
useEffect(() => {
if (!isLoggedIn || memberId == 0) {
if (memberId == 0) {
navigate('/');
}
}, [isLoggedIn, memberId, navigate]);
}, [memberId, navigate]);
const { data: workspacesResponse } = useWorkspaceListQuery(memberId ?? 0);
const createWorkspace = useCreateWorkspaceQuery();

View File

@ -7,12 +7,14 @@ import useCanvasStore from '@/stores/useCanvasStore';
import { Button } from '../ui/button';
import { useEffect } from 'react';
import WorkspaceDropdownMenu from '../WorkspaceDropdownMenu';
import useAutoLabelQuery from '@/queries/projects/useAutoLabelQuery';
import useProjectStore from '@/stores/useProjectStore';
export default function ProjectStructure({ project }: { project: Project }) {
const setProject = useProjectStore((state) => state.setProject);
const image = useCanvasStore((state) => state.image);
const { data: folderData, refetch } = useFolderQuery(project.id.toString(), 0);
const requestAutoLabel = useAutoLabelQuery();
useEffect(() => {
setProject(project);
@ -60,7 +62,17 @@ export default function ProjectStructure({ project }: { project: Project }) {
<Button
variant="outlinePrimary"
className="w-full"
onClick={() => console.log('autolabel')}
onClick={() => {
requestAutoLabel.mutate(
{ projectId: project.id },
{
onSuccess: refetch,
onError: () => {
alert('자동 레이블링을 요청하는 중 오류가 발생했습니다.');
},
}
);
}}
>
<Play
size={16}

View File

@ -4,11 +4,11 @@ import useProfileQuery from '@/queries/auth/useProfileQuery';
export default function useHandleOAuthCallback() {
const queryParams = new URLSearchParams(window.location.search);
const accessToken = queryParams.get('accessToken');
const setLoggedIn = useAuthStore((state) => state.setLoggedIn);
const setToken = useAuthStore((state) => state.setToken);
const setProfile = useAuthStore((state) => state.setProfile);
if (accessToken) {
setLoggedIn(true, accessToken);
setToken(accessToken);
}
const { data: profile } = useProfileQuery();

View File

@ -1,25 +1,12 @@
import { useRef } from 'react';
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 { getProfile } from '@/api/authApi';
const BASE_URL = import.meta.env.VITE_API_URL;
export default function Home() {
const { isLoggedIn, accessToken, setLoggedIn, profile, setProfile } = 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`;
};
const { accessToken } = useAuthStore();
return (
<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>
</div>
{!isLoggedIn ? (
<button
onClick={handleGoogleSignIn}
{!accessToken ? (
<Link
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"
>
<img
@ -52,7 +40,7 @@ export default function Home() {
alt="Sign in with Google"
className="h-auto w-full"
/>
</button> // 404 에러 방지
</Link> // 404 에러 방지
) : (
<>
<Button

View File

@ -4,12 +4,12 @@ import { reissueToken } from '@/api/authApi';
export default function useReissueTokenQuery() {
const queryClient = useQueryClient();
const { setLoggedIn } = useAuthStore();
const { setToken } = useAuthStore();
return useMutation({
mutationFn: reissueToken,
onSuccess: (data) => {
setLoggedIn(true, data.accessToken);
setToken(data.accessToken);
queryClient.invalidateQueries({ queryKey: ['profile'] });
},
});

View 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),
});
}

View File

@ -45,7 +45,6 @@ const router = createBrowserRouter([
],
},
{
// FIXME: index에서 오류나지 않게 수정
path: webPath.browse(),
element: (
<Suspense fallback={<PageLayout />}>
@ -64,7 +63,6 @@ const router = createBrowserRouter([
],
},
{
// FIXME: index에서 오류나지 않게 수정
path: `${webPath.workspace()}/:workspaceId`,
element: (
<Suspense fallback={<div></div>}>

View File

@ -3,10 +3,9 @@ import { persist } from 'zustand/middleware';
import { MemberResponse } from '@/types';
interface AuthState {
isLoggedIn: boolean;
accessToken: string;
profile: MemberResponse | null;
setLoggedIn: (status: boolean, token: string) => void;
setToken: (token: string) => void;
setProfile: (profile: MemberResponse) => void;
clearAuth: () => void;
}
@ -14,12 +13,11 @@ interface AuthState {
const useAuthStore = create<AuthState>()(
persist(
(set) => ({
isLoggedIn: false,
accessToken: '',
profile: null,
setLoggedIn: (status: boolean, token: string) => set({ isLoggedIn: status, accessToken: token }),
setToken: (token: string) => set({ accessToken: token }),
setProfile: (profile: MemberResponse) => set({ profile }),
clearAuth: () => set({ isLoggedIn: false, accessToken: '', profile: null }),
clearAuth: () => set({ accessToken: '', profile: null }),
}),
{
name: 'auth-storage',