refactor: 작업자 관리 페이지를 카드에서 테이블 형식으로 변경

## 변경 사항
- UI 형식: 카드 그리드 → 엑셀 스타일 테이블
- 더 많은 정보를 한눈에 볼 수 있음
- 공간 활용 효율성 향상

## HTML 변경 (workers.html)
- 테이블 구조 추가
  - 컬럼: 상태, 이름, 직책, 전화번호, 이메일, 입사일, 부서, 계정, 현장직, 등록일, 관리
  - tbody id="workersGrid" 유지 (기존 코드 호환성)

## JavaScript 변경 (worker-management.js)
- renderWorkers() 함수 리팩토링
  - 카드 HTML 생성 → 테이블 행 생성
  - 상태 배지: 현장직(초록), 사무직(노랑), 퇴사(빨강)
  - 아바타 아이콘 유지 (이름 첫 글자)
  - 아이콘 버튼으로 편집/상태변경/삭제 기능

## CSS 변경 (admin-pages.css)
- 테이블 내 버튼 스타일 추가
  - .data-table .btn-icon
  - hover 효과 및 transform

## 유지된 기능
- 검색 및 필터링
- 정렬
- 통계 표시
- 편집/삭제/상태 변경
- Empty state

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-01-26 15:24:33 +09:00
parent 45f80e206b
commit ad7088d840
3 changed files with 92 additions and 55 deletions

View File

@@ -813,6 +813,22 @@ tr:last-child td {
border-bottom: none;
}
/* 테이블 내 버튼 스타일 */
.data-table .btn-icon {
padding: 0.375rem 0.5rem;
background: transparent;
border: none;
cursor: pointer;
font-size: 1rem;
transition: all 0.2s ease;
border-radius: var(--radius-sm);
}
.data-table .btn-icon:hover {
background: var(--bg-hover);
transform: scale(1.1);
}
/* ============================================
11. 모달
============================================ */

View File

@@ -175,18 +175,19 @@ async function loadWorkers() {
function renderWorkers() {
const workersGrid = document.getElementById('workersGrid');
const emptyState = document.getElementById('emptyState');
const tableContainer = document.querySelector('.table-container');
if (!workersGrid || !emptyState) return;
if (filteredWorkers.length === 0) {
workersGrid.style.display = 'none';
emptyState.style.display = 'block';
if (tableContainer) tableContainer.style.display = 'none';
emptyState.style.display = 'flex';
return;
}
workersGrid.style.display = 'grid';
if (tableContainer) tableContainer.style.display = 'block';
emptyState.style.display = 'none';
const workersHtml = filteredWorkers.map(worker => {
// 작업자 상태 및 직책 아이콘
const jobTypeMap = {
@@ -194,66 +195,66 @@ function renderWorkers() {
'leader': { icon: '👨‍💼', text: '그룹장', color: '#3b82f6' },
'admin': { icon: '👨‍💻', text: '관리자', color: '#8b5cf6' }
};
const jobType = jobTypeMap[worker.job_type] || jobTypeMap['worker'];
const isInactive = worker.status === 'inactive' || worker.is_active === 0 || worker.is_active === false;
const isResigned = worker.employment_status === 'resigned';
const hasAccount = worker.user_id !== null && worker.user_id !== undefined;
console.log('🎨 카드 렌더링:', {
worker_id: worker.worker_id,
worker_name: worker.worker_name,
status: worker.status,
is_active: worker.is_active,
isInactive: isInactive,
isResigned: isResigned,
user_id: worker.user_id,
hasAccount: hasAccount
});
// 상태 배지
let statusBadge = '';
if (isResigned) {
statusBadge = '<span style="display: inline-block; padding: 0.25rem 0.5rem; background: #fee2e2; color: #dc2626; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600;">퇴사</span>';
} else if (isInactive) {
statusBadge = '<span style="display: inline-block; padding: 0.25rem 0.5rem; background: #fef3c7; color: #92400e; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600;">사무직</span>';
} else {
statusBadge = '<span style="display: inline-block; padding: 0.25rem 0.5rem; background: #d1fae5; color: #065f46; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600;">현장직</span>';
}
return `
<div class="project-card worker-card ${isResigned ? 'resigned' : ''} ${isInactive ? 'inactive' : ''}" onclick="editWorker(${worker.worker_id})">
${isResigned ? '<div class="inactive-overlay"><span class="inactive-badge" style="background: #dc2626;">🚪 퇴사</span></div>' :
isInactive ? '<div class="inactive-overlay"><span class="inactive-badge">🏢 사무직</span></div>' : ''}
<div class="project-header">
<div class="project-info">
<div class="worker-avatar">
<span class="avatar-initial">${worker.worker_name.charAt(0)}</span>
</div>
<h3 class="project-name">
${worker.worker_name}
${hasAccount ? '<span style="color: #10b981; font-size: 0.8rem; margin-left: 0.5rem;">🔐</span>' : ''}
${isResigned ? '<span class="inactive-label" style="color: #dc2626;">(퇴사)</span>' :
isInactive ? '<span class="inactive-label">(사무직)</span>' : ''}
</h3>
<div class="project-meta">
<span style="color: ${jobType.color}; font-weight: 500;">${jobType.icon} ${jobType.text}</span>
${hasAccount ? '<span style="color: #10b981;">🔐 계정 연동됨</span>' : '<span style="color: #9ca3af;">⚪ 계정 없음</span>'}
${worker.phone_number ? `<span>📞 ${worker.phone_number}</span>` : ''}
${worker.email ? `<span>📧 ${worker.email}</span>` : ''}
${worker.department ? `<span>🏢 ${worker.department}</span>` : ''}
${worker.hire_date ? `<span>📅 입사: ${formatDate(worker.hire_date)}</span>` : ''}
${isResigned ? '<span class="inactive-notice" style="color: #dc2626;">⚠️ 퇴사 처리됨</span>' : ''}
<tr class="${isResigned ? 'row-resigned' : ''}" style="${isResigned ? 'opacity: 0.6;' : ''}">
<td style="text-align: center;">${statusBadge}</td>
<td style="font-weight: 600;">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<div style="width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 0.875rem;">
${worker.worker_name.charAt(0)}
</div>
${worker.worker_name}
</div>
<div class="project-actions">
<button class="btn-toggle ${isInactive ? 'btn-activate' : 'btn-deactivate'}"
onclick="event.stopPropagation(); toggleWorkerStatus(${worker.worker_id})"
title="${isInactive ? '현장직으로 변경' : '사무직으로 변경'}">
${isInactive ? '🏭' : '🏢'}
</button>
<button class="btn-edit" onclick="event.stopPropagation(); editWorker(${worker.worker_id})" title="수정">
</td>
<td>
<span style="color: ${jobType.color}; font-weight: 500;">
${jobType.icon} ${jobType.text}
</span>
</td>
<td>${worker.phone_number || '-'}</td>
<td style="font-size: 0.875rem;">${worker.email || '-'}</td>
<td>${worker.hire_date ? formatDate(worker.hire_date) : '-'}</td>
<td>${worker.department || '-'}</td>
<td style="text-align: center;">
${hasAccount ? '<span style="color: #10b981; font-size: 1.25rem;" title="계정 연동됨">🔐</span>' : '<span style="color: #d1d5db; font-size: 1.25rem;" title="계정 없음">⚪</span>'}
</td>
<td style="text-align: center;">
${isInactive ? '<span style="color: #d1d5db; font-size: 1.25rem;">🏢</span>' : '<span style="color: #10b981; font-size: 1.25rem;">🏭</span>'}
</td>
<td style="font-size: 0.875rem; color: #6b7280;">${worker.created_at ? formatDate(worker.created_at) : '-'}</td>
<td>
<div style="display: flex; gap: 0.25rem; justify-content: center;">
<button class="btn-icon" onclick="editWorker(${worker.worker_id})" title="수정">
✏️
</button>
<button class="btn-delete" onclick="event.stopPropagation(); confirmDeleteWorker(${worker.worker_id})" title="삭제">
<button class="btn-icon" onclick="toggleWorkerStatus(${worker.worker_id})" title="${isInactive ? '현장직으로 변경' : '사무직으로 변경'}">
${isInactive ? '🏭' : '🏢'}
</button>
<button class="btn-icon" onclick="confirmDeleteWorker(${worker.worker_id})" title="삭제" style="color: #ef4444;">
🗑️
</button>
</div>
</div>
</div>
</td>
</tr>
`;
}).join('');
workersGrid.innerHTML = workersHtml;
}

View File

@@ -138,11 +138,31 @@
</span>
</div>
</div>
<div class="projects-grid" id="workersGrid">
<!-- 작업자 카드들이 여기에 동적으로 생성됩니다 -->
<!-- 작업자 테이블 -->
<div class="table-container">
<table class="data-table" id="workersTable">
<thead>
<tr>
<th style="width: 60px;">상태</th>
<th style="width: 100px;">이름</th>
<th style="width: 100px;">직책</th>
<th style="width: 130px;">전화번호</th>
<th style="width: 180px;">이메일</th>
<th style="width: 100px;">입사일</th>
<th style="width: 100px;">부서</th>
<th style="width: 80px;">계정</th>
<th style="width: 80px;">현장직</th>
<th style="width: 120px;">등록일</th>
<th style="width: 100px;">관리</th>
</tr>
</thead>
<tbody id="workersGrid">
<!-- 작업자 행들이 여기에 동적으로 생성됩니다 -->
</tbody>
</table>
</div>
<!-- Empty State -->
<div class="empty-state" id="emptyState" style="display: none;">
<div class="empty-icon">👥</div>