/* ============================================================ Root app — auth, hash-free state routing, role gating ============================================================ */ const { useState: useSR, useEffect: useER } = React; function BootSplash() { return (
LG AI 365
); } function loadRoute() { try { const r = JSON.parse(localStorage.getItem('lg_route')); if (r && r.name) return r; } catch (e) {} return { name: 'home' }; } function AppInner() { const toast = useToast(); const [user, setUser] = useSR(undefined); // undefined = checking const [route, setRoute] = useSR(loadRoute()); useER(() => { const t = localStorage.getItem('lg_token'); if (t) { window.api.setToken(t); window.api.me().then(setUser).catch(() => { setUser(null); }); } else setUser(null); }, []); const navigate = (r) => { setRoute(r); localStorage.setItem('lg_route', JSON.stringify(r)); }; const onLogin = ({ token, user }) => { localStorage.setItem('lg_token', token); setUser(user); navigate({ name: 'home' }); }; const onLogout = () => { window.api.logout(); localStorage.removeItem('lg_token'); localStorage.removeItem('lg_route'); setUser(null); setRoute({ name: 'home' }); }; // Context project for the viewer. 프로세스·WBS is GLOBAL but still needs a project // for context (project.id / periodStart), so always keep one: prefer the 운영가이드 // 표기(ON=active) project, else fall back to the first. The active check only gates // the 콘텐츠 tab below — WBS/프로세스 must never depend on it. const projQ = useAsync(() => (user ? window.api.getProjects().then((l) => l.find((p) => p.status === 'active') || l[0] || null) : Promise.resolve(null)), [user]); const project = projQ.data; if (user === undefined) return ; if (!user) return ; // role gate: LGE cannot reach admin screens let r = route; if (user.role === 'LGE' && (r.name === 'audit' || r.name === 'users' || r.name === 'eplog' || r.name === 'epadmin')) r = { name: 'home' }; if (user.role === 'LGE' && r.name === 'project' && r.tab === 'history') r = { ...r, tab: 'episodes' }; let content; if (r.name === 'audit') content = ; else if (r.name === 'eplog') content = ; else if (r.name === 'epadmin') content = ; else if (r.name === 'users') content = ; else { // home/project → the single operation if (projQ.loading) content = ; else if (projQ.error) content =
; else if (project) { const m = r.month || (project.months[0] && project.months[0].month) || null; const common = { project, month: m, user, navigate, toast }; // 콘텐츠 tab only shows when a project is 표기 ON(active); otherwise "운영 중인 가이드 // 없음". 프로세스·WBS (else) and history always render against the context project. if (r.tab === 'episodes') content = project.status === 'active' ? :
} title="운영 중인 가이드가 없습니다" desc="운영가이드 표기(ON)된 운영 가이드가 없습니다." />
; else if (r.tab === 'history') content = ; else content = ; } else content =
} title="운영 중인 가이드가 없습니다" desc="운영가이드 표기(ON)된 운영 가이드가 없습니다." />
; } return ( {content} ); } function ProjectLoading() { return (
{[0, 1].map((i) => (
{[0, 1, 2].map((j) => )}
))}
); } function App() { return ; } ReactDOM.createRoot(document.getElementById('root')).render();