feat: 일일순회점검 시스템 구축 및 관리 기능 개선
- 일일순회점검 시스템 신규 구현 - DB 테이블: patrol_checklist_items, daily_patrol_sessions, patrol_check_records, workplace_items, item_types - API: /api/patrol/* 엔드포인트 - 프론트엔드: 지도 기반 작업장 점검 UI - 설비 관리 기능 개선 - 구매 관련 필드 추가 (구매일, 가격, 공급업체 등) - 설비 코드 자동 생성 (TKP-XXX 형식) - 작업장 관리 개선 - 레이아웃 이미지 업로드 기능 - 마커 위치 저장 기능 - 부서 관리 기능 추가 - 사이드바 네비게이션 카테고리 재구성 - 이미지 401 오류 수정 (정적 파일 경로 처리) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
156
web-ui/js/tbm.js
156
web-ui/js/tbm.js
@@ -118,8 +118,8 @@ async function loadInitialData() {
|
||||
currentUser = userInfo;
|
||||
console.log('👤 로그인 사용자:', currentUser, 'worker_id:', currentUser?.worker_id);
|
||||
|
||||
// 작업자 목록 로드
|
||||
const workersResponse = await window.apiCall('/workers?limit=1000');
|
||||
// 작업자 목록 로드 (생산팀 소속만)
|
||||
const workersResponse = await window.apiCall('/workers?limit=1000&department_id=1');
|
||||
if (workersResponse) {
|
||||
allWorkers = Array.isArray(workersResponse) ? workersResponse : (workersResponse.data || []);
|
||||
// 활성 상태인 작업자만 필터링
|
||||
@@ -185,7 +185,7 @@ function switchTbmTab(tabName) {
|
||||
currentTab = tabName;
|
||||
|
||||
// 탭 버튼 활성화 상태 변경
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
document.querySelectorAll('.tbm-tab-btn').forEach(btn => {
|
||||
if (btn.dataset.tab === tabName) {
|
||||
btn.classList.add('active');
|
||||
} else {
|
||||
@@ -194,7 +194,7 @@ function switchTbmTab(tabName) {
|
||||
});
|
||||
|
||||
// 탭 컨텐츠 표시 변경
|
||||
document.querySelectorAll('.code-tab-content').forEach(content => {
|
||||
document.querySelectorAll('.tbm-tab-content').forEach(content => {
|
||||
content.classList.remove('active');
|
||||
});
|
||||
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||||
@@ -409,20 +409,33 @@ function displayTbmGroupedByDate() {
|
||||
const displayDate = `${parseInt(month)}월 ${parseInt(day)}일`;
|
||||
|
||||
return `
|
||||
<div class="date-group">
|
||||
<div class="date-group-header ${isToday ? 'today' : ''}">
|
||||
<span class="date-group-date">${displayDate}</span>
|
||||
<span class="date-group-day">${dayName}요일${isToday ? ' (오늘)' : ''}</span>
|
||||
<span class="date-group-count">${sessions.length}건</span>
|
||||
<div class="tbm-date-group" data-date="${date}">
|
||||
<div class="tbm-date-header ${isToday ? 'today' : ''}" onclick="toggleDateGroup('${date}')">
|
||||
<span class="tbm-date-toggle">▼</span>
|
||||
<span class="tbm-date-title">${displayDate}</span>
|
||||
<span class="tbm-date-day">${dayName}요일</span>
|
||||
${isToday ? '<span class="tbm-today-badge">오늘</span>' : ''}
|
||||
<span class="tbm-date-count">${sessions.length}건</span>
|
||||
</div>
|
||||
<div class="date-group-grid">
|
||||
${sessions.map(session => createSessionCard(session)).join('')}
|
||||
<div class="tbm-date-content">
|
||||
<div class="tbm-date-grid">
|
||||
${sessions.map(session => createSessionCard(session)).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// 날짜 그룹 토글
|
||||
function toggleDateGroup(date) {
|
||||
const group = document.querySelector(`.tbm-date-group[data-date="${date}"]`);
|
||||
if (group) {
|
||||
group.classList.toggle('collapsed');
|
||||
}
|
||||
}
|
||||
window.toggleDateGroup = toggleDateGroup;
|
||||
|
||||
/**
|
||||
* 더 많은 날짜 로드
|
||||
*/
|
||||
@@ -474,73 +487,66 @@ function displayTbmSessions() {
|
||||
// TBM 세션 카드 생성 (공통)
|
||||
function createSessionCard(session) {
|
||||
const statusBadge = {
|
||||
'draft': '<span class="badge" style="background: #fef3c7; color: #92400e;">진행중</span>',
|
||||
'completed': '<span class="badge" style="background: #dcfce7; color: #166534;">완료</span>',
|
||||
'cancelled': '<span class="badge" style="background: #fee2e2; color: #991b1b;">취소</span>'
|
||||
'draft': '<span class="tbm-card-status draft">진행중</span>',
|
||||
'completed': '<span class="tbm-card-status completed">완료</span>',
|
||||
'cancelled': '<span class="tbm-card-status cancelled">취소</span>'
|
||||
}[session.status] || '';
|
||||
|
||||
// 작업 책임자 표시 (leader_name이 있으면 표시, 없으면 created_by_name 표시)
|
||||
const leaderDisplay = session.leader_name
|
||||
? `${session.leader_name} (${session.leader_job_type || '작업자'})`
|
||||
: `${session.created_by_name || '작업 책임자'} (관리자)`;
|
||||
const leaderName = session.leader_name || session.created_by_name || '작업 책임자';
|
||||
const leaderRole = session.leader_name
|
||||
? (session.leader_job_type || '작업자')
|
||||
: '관리자';
|
||||
|
||||
return `
|
||||
<div class="project-card" style="cursor: pointer;" onclick="viewTbmSession(${session.session_id})">
|
||||
<div class="project-header">
|
||||
<div>
|
||||
<h3 class="project-name" style="font-size: 1rem; margin-bottom: 0.25rem;">
|
||||
${leaderDisplay}
|
||||
</h3>
|
||||
<p style="font-size: 0.75rem; color: #6b7280; margin: 0;">
|
||||
${formatDate(session.session_date)}
|
||||
</p>
|
||||
<div class="tbm-session-card" onclick="viewTbmSession(${session.session_id})">
|
||||
<div class="tbm-card-header">
|
||||
<div class="tbm-card-header-top">
|
||||
<div>
|
||||
<h3 class="tbm-card-leader">
|
||||
${leaderName}
|
||||
<span class="tbm-card-leader-role">${leaderRole}</span>
|
||||
</h3>
|
||||
</div>
|
||||
${statusBadge}
|
||||
</div>
|
||||
${statusBadge}
|
||||
</div>
|
||||
|
||||
<div class="project-info" style="margin-top: 1rem;">
|
||||
<div class="info-item">
|
||||
<span class="info-label">프로젝트</span>
|
||||
<span class="info-value">${session.project_name || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">공정</span>
|
||||
<span class="info-value">${session.work_type_name || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">작업</span>
|
||||
<span class="info-value">${session.task_name || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">작업 장소</span>
|
||||
<span class="info-value">${session.work_location || '-'}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">팀원 수</span>
|
||||
<span class="info-value">${session.team_member_count || 0}명</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">시작 시간</span>
|
||||
<span class="info-value">${session.start_time || '-'}</span>
|
||||
<div class="tbm-card-date">
|
||||
<span>📅</span>
|
||||
${formatDate(session.session_date)} ${session.start_time ? '| ' + session.start_time : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${session.work_description ? `
|
||||
<div style="margin-top: 0.75rem; padding: 0.75rem; background: #f9fafb; border-radius: 0.375rem; font-size: 0.875rem; color: #374151;">
|
||||
${session.work_description}
|
||||
<div class="tbm-card-body">
|
||||
<div class="tbm-card-info-grid">
|
||||
<div class="tbm-card-info-item">
|
||||
<span class="tbm-card-info-label">프로젝트</span>
|
||||
<span class="tbm-card-info-value">${session.project_name || '-'}</span>
|
||||
</div>
|
||||
<div class="tbm-card-info-item">
|
||||
<span class="tbm-card-info-label">공정</span>
|
||||
<span class="tbm-card-info-value">${session.work_type_name || '-'}</span>
|
||||
</div>
|
||||
<div class="tbm-card-info-item">
|
||||
<span class="tbm-card-info-label">작업장</span>
|
||||
<span class="tbm-card-info-value">${session.work_location || '-'}</span>
|
||||
</div>
|
||||
<div class="tbm-card-info-item">
|
||||
<span class="tbm-card-info-label">팀원</span>
|
||||
<span class="tbm-card-info-value">${session.team_member_count || 0}명</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${session.status === 'draft' ? `
|
||||
<div class="tbm-card-footer">
|
||||
<button class="tbm-btn tbm-btn-primary tbm-btn-sm" onclick="event.stopPropagation(); openTeamCompositionModal(${session.session_id})">
|
||||
👥 팀 구성
|
||||
</button>
|
||||
<button class="tbm-btn tbm-btn-secondary tbm-btn-sm" onclick="event.stopPropagation(); openSafetyCheckModal(${session.session_id})">
|
||||
✓ 안전 체크
|
||||
</button>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div style="margin-top: 1rem; display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||||
${session.status === 'draft' ? `
|
||||
<button class="btn btn-sm btn-primary" onclick="event.stopPropagation(); openTeamCompositionModal(${session.session_id})" style="flex: 1; min-width: 100px;">
|
||||
👥 팀 구성
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="event.stopPropagation(); openSafetyCheckModal(${session.session_id})" style="flex: 1; min-width: 100px;">
|
||||
✅ 안전 체크
|
||||
</button>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -550,23 +556,33 @@ function openNewTbmModal() {
|
||||
currentSessionId = null;
|
||||
workerTaskList = []; // 작업자 목록 초기화
|
||||
|
||||
document.getElementById('modalTitle').textContent = '새 TBM 시작';
|
||||
document.getElementById('modalTitle').innerHTML = '<span>📝</span> 새 TBM 시작';
|
||||
document.getElementById('sessionId').value = '';
|
||||
document.getElementById('tbmForm').reset();
|
||||
|
||||
const today = getTodayKST();
|
||||
document.getElementById('sessionDate').value = today;
|
||||
|
||||
// 날짜 표시 업데이트
|
||||
const [year, month, day] = today.split('-');
|
||||
const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
|
||||
const dateObj = new Date(today);
|
||||
const dayName = dayNames[dateObj.getDay()];
|
||||
const sessionDateDisplay = document.getElementById('sessionDateDisplay');
|
||||
if (sessionDateDisplay) {
|
||||
sessionDateDisplay.textContent = `${year}년 ${parseInt(month)}월 ${parseInt(day)}일 (${dayName})`;
|
||||
}
|
||||
|
||||
// 입력자 자동 설정 (readonly)
|
||||
if (currentUser && currentUser.worker_id) {
|
||||
const worker = allWorkers.find(w => w.worker_id === currentUser.worker_id);
|
||||
if (worker) {
|
||||
document.getElementById('leaderName').value = worker.worker_name;
|
||||
document.getElementById('leaderName').textContent = worker.worker_name;
|
||||
document.getElementById('leaderId').value = worker.worker_id;
|
||||
}
|
||||
} else if (currentUser && currentUser.name) {
|
||||
// 관리자: 이름만 표시
|
||||
document.getElementById('leaderName').value = currentUser.name;
|
||||
document.getElementById('leaderName').textContent = currentUser.name;
|
||||
document.getElementById('leaderId').value = '';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user