// shell.jsx — sidebar + topbar + page wrapper const { Icon, Pill, Avatar } = window.RAlly; const NAV_GROUPS = [ { label: 'Workspace', items: [ { key: 'intake', icon: 'flag', label: '시작하기' }, // AI 어시스턴트의 주 진입점은 우측 상주 패널. 이 메뉴는 넓은 화면에서 대화만 크게 보고 싶을 때 쓰는 전체화면 뷰. { key: 'chat', icon: 'message', label: 'AI 대화 · 전체화면', title: 'AI 어시스턴트는 오른쪽 패널에 항상 떠 있습니다. 이 메뉴는 대화만 크게 보는 전체화면 뷰입니다.' }, { key: 'dashboard', icon: 'home', label: '대시보드' }, { key: 'tracks', icon: 'layers', label: '트랙' }, ], }, { label: 'Documents & Quality', items: [ { key: 'docs', icon: 'file', label: '문서' }, { key: 'fill', icon: 'edit', label: '정보 입력' }, { key: 'templates', icon: 'bookmark', label: '템플릿' }, { key: 'audit', icon: 'shield', label: '가상 심사' }, { key: 'capa', icon: 'beaker', label: 'CAPA', dot: true }, ], }, { label: 'Certification', items: [ { key: 'cert', icon: 'trophy', label: '인증 패키지' }, { key: 'share', icon: 'link', label: '외부 공유' }, ], }, ]; // Mapping nav item key → app screen const NAV_TO_SCREEN = { intake: 'intake', chat: 'chat', dashboard: 'dashboard', tracks: 'track', docs: 'kanban', fill: 'fill', templates: 'templates', audit: 'audit', risk: 'risk', capa: 'capa', cert: 'cert', share: 'share', }; const Sidebar = ({ screen, go, user, logout }) => { const u = user || (window.RAlly.getUser && window.RAlly.getUser()) || { name: '사용자', roleLabel: '', org: '', initials: '··' }; // Compute the active nav key from current screen const screenToNav = { intake: 'intake', chat: 'chat', dashboard: 'dashboard', track: 'tracks', kanban: 'docs', editor: 'docs', fill: 'fill', templates: 'templates', audit: 'audit', risk: 'risk', capa: 'capa', cert: 'cert', share: 'share', }; const activeNav = screenToNav[screen]; // 실데이터 카운트(하드코딩 제거) const [counts, setCounts] = React.useState({}); React.useEffect(() => { fetch('/api/tracks').then((r) => r.json()).then((d) => { setCounts((c) => ({ ...c, tracks: (d.tracks || []).length })); }).catch(() => {}); fetch('/api/catalog').then((r) => r.json()).then((d) => { if (d.ok) setCounts((c) => ({ ...c, docs: d.counts.total, templates: d.counts.total })); }).catch(() => {}); }, []); return ( ); }; const Topbar = ({ crumbs, right }) => (
{crumbs.map((c, i) => ( {i > 0 && } {c} ))}
⌘K
{right}
); window.RAlly.Sidebar = Sidebar; window.RAlly.Topbar = Topbar;