wing-ops/frontend/src/App.tsx

159 lines
4.3 KiB
TypeScript
Executable File

import { useState, useEffect } from 'react';
import { Routes, Route } from 'react-router-dom';
import { GoogleOAuthProvider } from '@react-oauth/google';
import type { MainTab } from '@common/types/navigation';
import { MainLayout } from '@common/components/layout/MainLayout';
import { LoginPage } from '@common/components/auth/LoginPage';
import { registerMainTabSwitcher } from '@common/hooks/useSubMenu';
import { useAuthStore } from '@common/store/authStore';
import { useMenuStore } from '@common/store/menuStore';
import { useMapStore } from '@common/store/mapStore';
import { API_BASE_URL } from '@common/services/api';
import { OilSpillView } from '@tabs/prediction';
import { ReportsView } from '@tabs/reports';
import { HNSView } from '@tabs/hns';
import { AerialView } from '@tabs/aerial';
import { AssetsView } from '@tabs/assets';
import { BoardView } from '@tabs/board';
import { WeatherView } from '@tabs/weather';
import { IncidentsView } from '@tabs/incidents';
import { AdminView } from '@tabs/admin';
import { ScatView } from '@tabs/scat';
import { RescueView } from '@tabs/rescue';
import { DesignPage } from '@/pages/design/DesignPage';
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID || '';
function App() {
const [activeMainTab, setActiveMainTab] = useState<MainTab>('prediction');
const { isAuthenticated, isLoading, checkSession } = useAuthStore();
const { loadMenuConfig } = useMenuStore();
const { loadMapTypes } = useMapStore();
useEffect(() => {
checkSession();
}, [checkSession]);
useEffect(() => {
if (isAuthenticated) {
loadMenuConfig();
loadMapTypes();
}
}, [isAuthenticated, loadMenuConfig, loadMapTypes]);
useEffect(() => {
registerMainTabSwitcher(setActiveMainTab);
}, []);
// 감사 로그: 탭 이동 기록
useEffect(() => {
if (!isAuthenticated) return;
const blob = new Blob([JSON.stringify({ action: 'TAB_VIEW', detail: activeMainTab })], {
type: 'text/plain',
});
navigator.sendBeacon(`${API_BASE_URL}/audit/log`, blob);
}, [activeMainTab, isAuthenticated]);
// 세션 확인 중 스플래시
if (isLoading) {
return (
<div
style={{
width: '100vw',
height: '100vh',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
background: 'var(--bg-base)',
gap: 16,
}}
>
<img
src="/wing_logo_text_white.svg"
alt="WING"
className="wing-logo"
style={{ height: 28, opacity: 0.8 }}
/>
<div
style={{
width: 32,
height: 32,
border: '3px solid rgba(6,182,212,0.2)',
borderTop: '3px solid rgba(6,182,212,0.8)',
borderRadius: '50%',
animation: 'loginSpin 0.8s linear infinite',
}}
/>
</div>
);
}
// 미인증 → 로그인 페이지
if (!isAuthenticated) {
return <LoginPage />;
}
const renderView = () => {
switch (activeMainTab) {
case 'prediction':
return <OilSpillView />;
case 'reports':
return <ReportsView />;
case 'hns':
return <HNSView />;
case 'aerial':
return <AerialView />;
case 'assets':
return <AssetsView />;
case 'board':
return <BoardView />;
case 'weather':
return <WeatherView />;
case 'incidents':
return <IncidentsView />;
case 'scat':
return <ScatView />;
case 'admin':
return <AdminView />;
case 'rescue':
return <RescueView />;
case 'monitor':
return null;
default:
return (
<div className="flex items-center justify-center h-full text-fg-disabled">
...
</div>
);
}
};
return (
<Routes>
<Route path="/design" element={<DesignPage />} />
<Route
path="*"
element={
<MainLayout activeMainTab={activeMainTab} onMainTabChange={setActiveMainTab}>
{renderView()}
</MainLayout>
}
/>
</Routes>
);
}
function AppWithProviders() {
if (!GOOGLE_CLIENT_ID) {
return <App />;
}
return (
<GoogleOAuthProvider clientId={GOOGLE_CLIENT_ID}>
<App />
</GoogleOAuthProvider>
);
}
export default AppWithProviders;