fix: [pmis] BIM 뷰어 사이드바 네비게이션 개선
- 정적 BimSidebar를 인터랙티브 PmisSidebar로 교체 - 아코디언 메뉴 토글 기능 추가 (시공관리, 안전관리 등) - 하위 항목을 클릭 가능한 링크로 변경 - 자료실 메뉴 누락분 추가 - 현재 페이지(BIM 뷰어) 활성 상태 표시
This commit is contained in:
@@ -579,33 +579,48 @@ class BimScene {
|
||||
React Components
|
||||
════════════════════════════════════════════════ */
|
||||
|
||||
function BimSidebar() {
|
||||
const PMIS_MENUS = [
|
||||
{ icon: 'ri-building-2-line', label: 'BIM 관리', id: 'bim', children: [
|
||||
{ label: 'BIM 뷰어', id: 'bim-viewer', url: '/juil/construction-pmis/bim-viewer' },
|
||||
]},
|
||||
{ icon: 'ri-line-chart-line', label: '시공관리', id: 'construction', children: [
|
||||
{ label: '인원관리', id: 'workforce', url: '/juil/construction-pmis/workforce' },
|
||||
{ label: '장비관리', id: 'equipment', url: '/juil/construction-pmis/equipment' },
|
||||
{ label: '자재관리', id: 'materials', url: '/juil/construction-pmis/materials' },
|
||||
{ label: '공사량관리', id: 'work-volume', url: '/juil/construction-pmis/work-volume' },
|
||||
{ label: '출면일보', id: 'daily-attendance', url: '/juil/construction-pmis/daily-attendance' },
|
||||
{ label: '작업일보', id: 'daily-report', url: '/juil/construction-pmis/daily-report' },
|
||||
]},
|
||||
{ icon: 'ri-file-list-3-line', label: '품질관리', id: 'quality', children: [
|
||||
{ label: '시정조치', id: 'corrective-action', url: '/juil/construction-pmis/corrective-action' },
|
||||
]},
|
||||
{ icon: 'ri-shield-check-line', label: '안전관리', id: 'safety', children: [
|
||||
{ label: '안전보건교육', id: 'safety-education', url: '/juil/construction-pmis/safety-education' },
|
||||
{ label: 'TBM현장', id: 'tbm', url: '/juil/construction-pmis/tbm' },
|
||||
{ label: '위험성 평가', id: 'risk-assessment', url: '/juil/construction-pmis/risk-assessment' },
|
||||
{ label: '재해예방조치', id: 'disaster-prevention', url: '/juil/construction-pmis/disaster-prevention' },
|
||||
]},
|
||||
{ icon: 'ri-folder-line', label: '자료실', id: 'archive', children: [
|
||||
{ label: '자료보관함', id: 'archive-files', url: '/juil/construction-pmis/archive-files' },
|
||||
{ label: '매뉴얼', id: 'archive-manual', url: '/juil/construction-pmis/archive-manual' },
|
||||
{ label: '공지사항', id: 'archive-notice', url: '/juil/construction-pmis/archive-notice' },
|
||||
]},
|
||||
];
|
||||
|
||||
function PmisSidebar({ activePage }) {
|
||||
const [profile, setProfile] = useState(null);
|
||||
const [expanded, setExpanded] = useState(() => {
|
||||
for (const m of PMIS_MENUS) {
|
||||
if (m.children?.some(c => c.id === activePage)) return m.id;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/juil/construction-pmis/profile', { headers: { Accept: 'application/json' } })
|
||||
.then(r => r.json()).then(d => setProfile(d.worker)).catch(() => {});
|
||||
}, []);
|
||||
const menus = [
|
||||
{ icon: 'ri-building-2-line', label: 'BIM 관리', active: true, children: [{ label: 'BIM 뷰어', active: true }] },
|
||||
{ icon: 'ri-line-chart-line', label: '시공관리', children: [
|
||||
{ label: '인원관리', url: '/juil/construction-pmis/workforce' },
|
||||
{ label: '장비관리', url: '/juil/construction-pmis/equipment' },
|
||||
{ label: '자재관리', url: '/juil/construction-pmis/materials' },
|
||||
{ label: '공사량관리', url: '/juil/construction-pmis/work-volume' },
|
||||
{ label: '출면일보', url: '/juil/construction-pmis/daily-attendance' },
|
||||
{ label: '작업일보', url: '/juil/construction-pmis/daily-report' },
|
||||
]},
|
||||
{ icon: 'ri-file-list-3-line', label: '품질관리', id: 'quality', children: [
|
||||
{ label: '시정조치', id: 'corrective-action', url: '/juil/construction-pmis/corrective-action' },
|
||||
]},
|
||||
{ icon: 'ri-shield-check-line', label: '안전관리', id: 'safety', children: [
|
||||
{ label: '안전보건교육', id: 'safety-education', url: '/juil/construction-pmis/safety-education' },
|
||||
{ label: 'TBM현장', id: 'tbm', url: '/juil/construction-pmis/tbm' },
|
||||
{ label: '위험성 평가', id: 'risk-assessment', url: '/juil/construction-pmis/risk-assessment' },
|
||||
{ label: '재해예방조치', id: 'disaster-prevention', url: '/juil/construction-pmis/disaster-prevention' },
|
||||
]},
|
||||
{ icon: 'ri-folder-line', label: '자료실' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white border-r border-gray-200 shadow-sm flex flex-col shrink-0" style={{ width: 200 }}>
|
||||
<a href="/juil/construction-pmis" className="flex items-center gap-2 px-4 py-3 text-sm text-blue-600 hover:bg-blue-50 border-b border-gray-100 transition">
|
||||
@@ -613,22 +628,27 @@ function BimSidebar() {
|
||||
</a>
|
||||
<div className="p-3 border-b border-gray-100 text-center">
|
||||
<div className="w-12 h-12 mx-auto mb-1 rounded-full bg-gray-100 border-2 border-gray-200 flex items-center justify-center">
|
||||
{profile?.profile_photo_path ? <img src={profile.profile_photo_path} className="w-full h-full rounded-full object-cover" /> : <i className="ri-user-3-line text-xl text-gray-300"></i>}
|
||||
{profile?.profile_photo_path
|
||||
? <img src={profile.profile_photo_path} className="w-full h-full rounded-full object-cover" />
|
||||
: <i className="ri-user-3-line text-xl text-gray-300"></i>}
|
||||
</div>
|
||||
<div className="text-sm font-bold text-gray-800">{profile?.name || '...'}</div>
|
||||
<div className="text-xs text-gray-500 mt-0.5">{profile?.department || ''}</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto py-1">
|
||||
{menus.map(m => (
|
||||
<div key={m.label}>
|
||||
<div className={`flex items-center gap-2 px-4 py-2.5 text-sm cursor-pointer transition ${m.active ? 'bg-blue-50 text-blue-700 font-semibold' : 'text-gray-600 hover:bg-gray-50'}`}>
|
||||
{PMIS_MENUS.map(m => (
|
||||
<div key={m.id}>
|
||||
<div
|
||||
onClick={() => setExpanded(expanded === m.id ? null : m.id)}
|
||||
className={`flex items-center gap-2 px-4 py-2.5 text-sm cursor-pointer transition ${expanded === m.id ? 'bg-blue-50 text-blue-700 font-semibold' : 'text-gray-600 hover:bg-gray-50'}`}>
|
||||
<i className={`${m.icon} text-base`}></i> {m.label}
|
||||
<i className={`ml-auto ${m.active ? 'ri-arrow-down-s-line' : 'ri-arrow-right-s-line'} text-gray-400 text-xs`}></i>
|
||||
<i className={`ml-auto ${expanded === m.id ? 'ri-arrow-down-s-line' : 'ri-arrow-right-s-line'} text-gray-400 text-xs`}></i>
|
||||
</div>
|
||||
{m.active && m.children?.map(c => (
|
||||
<div key={c.label} className={`pl-10 pr-4 py-2 text-sm ${c.active ? 'bg-blue-100 text-blue-800 font-semibold border-l-2 border-blue-600' : 'text-gray-500 hover:text-blue-600'}`}>
|
||||
{expanded === m.id && m.children?.map(c => (
|
||||
<a key={c.id} href={c.url}
|
||||
className={`block pl-10 pr-4 py-2 text-sm transition ${c.id === activePage ? 'bg-blue-100 text-blue-800 font-semibold border-l-2 border-blue-600' : 'text-gray-500 hover:text-blue-600 hover:bg-gray-50'}`}>
|
||||
{c.label}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
@@ -888,7 +908,7 @@ function BimViewerApp() {
|
||||
|
||||
return (
|
||||
<div className="flex bg-gray-100" style={{ height: 'calc(100vh - 56px)' }}>
|
||||
<BimSidebar />
|
||||
<PmisSidebar activePage="bim-viewer" />
|
||||
<div className="flex-1 flex flex-col relative overflow-hidden"
|
||||
onDragOver={onDragOver} onDragLeave={onDragLeave} onDrop={onDrop}>
|
||||
<div ref={vpRef} className="flex-1" style={{ minHeight: 0 }} />
|
||||
|
||||
Reference in New Issue
Block a user