- 시공관리 하위메뉴: 인원관리, 장비관리, 자재관리, 공사량관리, 출면일보, 작업일보 - 인원관리 4개 탭 구현: 인원등록, 출역현황, 투입현황(업체별), 투입현황(근로자별) - PMIS 사이드바에 시공관리 children 메뉴 추가 (대시보드, BIM 뷰어 포함) - 나머지 5개 메뉴 placeholder 페이지 생성
118 lines
6.2 KiB
PHP
118 lines
6.2 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '공사량관리 - 건설PMIS')
|
|
|
|
@section('content')
|
|
<div id="root"></div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet" />
|
|
@include('partials.react-cdn')
|
|
<script type="text/babel">
|
|
@verbatim
|
|
const { useState, useEffect, useRef, useCallback, useMemo } = React;
|
|
|
|
/* ════════════════════════════════════════════════
|
|
PMIS 사이드바
|
|
════════════════════════════════════════════════ */
|
|
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' },
|
|
{ icon: 'ri-shield-check-line', label: '안전관리', id: 'safety' },
|
|
{ icon: 'ri-folder-line', label: '자료실', id: 'archive' },
|
|
];
|
|
|
|
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(() => {});
|
|
}, []);
|
|
|
|
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">
|
|
<i className="ri-arrow-left-s-line text-lg"></i> PMIS 대시보드
|
|
</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>}
|
|
</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">
|
|
{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 ${expanded === m.id ? 'ri-arrow-down-s-line' : 'ri-arrow-right-s-line'} text-gray-400 text-xs`}></i>
|
|
</div>
|
|
{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}
|
|
</a>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/* ════════════════════════════════════════════════
|
|
메인 컴포넌트
|
|
════════════════════════════════════════════════ */
|
|
function App() {
|
|
return (
|
|
<div className="flex bg-gray-100" style={{ height: 'calc(100vh - 56px)' }}>
|
|
<PmisSidebar activePage="work-volume" />
|
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
<div className="bg-white border-b border-gray-200 px-6 py-4">
|
|
<div className="flex items-center gap-2 text-xs text-gray-400 mb-2">
|
|
<i className="ri-home-4-line"></i>
|
|
<span>Home</span> > <span>시공관리</span> > <span className="text-gray-600">공사량관리</span>
|
|
</div>
|
|
<h1 className="text-lg font-bold text-gray-800">공사량관리</h1>
|
|
</div>
|
|
<div className="flex-1 flex items-center justify-center">
|
|
<div className="text-center text-gray-400">
|
|
<i className="ri-tools-line text-5xl block mb-3"></i>
|
|
<p className="text-lg font-semibold">공사량관리</p>
|
|
<p className="text-sm mt-1">준비 중입니다</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.render(<App />, document.getElementById('root'));
|
|
@endverbatim
|
|
</script>
|
|
@endpush
|