feat: 안전 코드 tksafety 이관 + 사용자 관리 정리 + UI Tailwind 전환
Phase 1: tksafety에 출입신청/체크리스트 API·웹 추가, tkfb 안전 코드 삭제
Phase 2: 사용자 관리 페이지 삭제, API 축소, 알림 수신자 tkuser 이관
Phase 3: tkuser 권한 페이지 정의 업데이트
Phase 4: 전체 34개 페이지 Tailwind CSS + tkfb-core.js 전환,
미사용 CSS 20개·인프라 JS 10개·템플릿·컴포넌트 삭제
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,9 +10,6 @@ let canvasImage = null;
|
||||
// 금일 TBM 작업자 데이터
|
||||
let todayWorkers = [];
|
||||
|
||||
// 금일 출입 신청 데이터
|
||||
let todayVisitors = [];
|
||||
|
||||
// ==================== 초기화 ====================
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
@@ -175,8 +172,6 @@ async function loadTodayData() {
|
||||
// TBM 작업자 데이터 로드
|
||||
await loadTodayWorkers(today);
|
||||
|
||||
// 출입 신청 데이터 로드
|
||||
await loadTodayVisitors(today);
|
||||
}
|
||||
|
||||
async function loadTodayWorkers(date) {
|
||||
@@ -212,43 +207,6 @@ async function loadTodayWorkers(date) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTodayVisitors(date) {
|
||||
try {
|
||||
// 날짜 형식 확인 (YYYY-MM-DD)
|
||||
const formattedDate = date.split('T')[0];
|
||||
|
||||
const response = await window.apiCall(`/workplace-visits/requests`, 'GET');
|
||||
|
||||
if (response && response.success) {
|
||||
const requests = response.data || [];
|
||||
|
||||
// 금일 날짜와 승인된 요청 필터링
|
||||
todayVisitors = requests.filter(req => {
|
||||
// UTC 변환 없이 로컬 날짜로 비교
|
||||
const visitDateObj = new Date(req.visit_date);
|
||||
const visitYear = visitDateObj.getFullYear();
|
||||
const visitMonth = String(visitDateObj.getMonth() + 1).padStart(2, '0');
|
||||
const visitDay = String(visitDateObj.getDate()).padStart(2, '0');
|
||||
const visitDate = `${visitYear}-${visitMonth}-${visitDay}`;
|
||||
|
||||
return visitDate === formattedDate &&
|
||||
(req.status === 'approved' || req.status === 'training_completed');
|
||||
}).map(req => ({
|
||||
workplace_id: req.workplace_id,
|
||||
visitor_company: req.visitor_company,
|
||||
visitor_count: req.visitor_count,
|
||||
visit_time: req.visit_time,
|
||||
purpose_name: req.purpose_name,
|
||||
status: req.status
|
||||
}));
|
||||
|
||||
console.log('로드된 방문자:', todayVisitors);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('출입 신청 데이터 로드 오류:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 지도 렌더링 ====================
|
||||
|
||||
function renderMap() {
|
||||
@@ -260,19 +218,17 @@ function renderMap() {
|
||||
|
||||
// 모든 작업장 영역 표시
|
||||
mapRegions.forEach(region => {
|
||||
// 해당 작업장의 작업자/방문자 인원 계산
|
||||
// 해당 작업장의 작업자 인원 계산
|
||||
const workers = todayWorkers.filter(w => w.workplace_id === region.workplace_id);
|
||||
const visitors = todayVisitors.filter(v => v.workplace_id === region.workplace_id);
|
||||
|
||||
const totalWorkerCount = workers.reduce((sum, w) => sum + (w.member_count || 0), 0);
|
||||
const totalVisitorCount = visitors.reduce((sum, v) => sum + (v.visitor_count || 0), 0);
|
||||
|
||||
// 영역 그리기
|
||||
drawWorkplaceRegion(region, totalWorkerCount, totalVisitorCount);
|
||||
drawWorkplaceRegion(region, totalWorkerCount);
|
||||
});
|
||||
}
|
||||
|
||||
function drawWorkplaceRegion(region, workerCount, visitorCount) {
|
||||
function drawWorkplaceRegion(region, workerCount) {
|
||||
// 사각형 좌표 변환
|
||||
const x1 = (region.x_start / 100) * canvas.width;
|
||||
const y1 = (region.y_start / 100) * canvas.height;
|
||||
@@ -286,20 +242,12 @@ function drawWorkplaceRegion(region, workerCount, visitorCount) {
|
||||
|
||||
// 색상 결정
|
||||
let fillColor, strokeColor;
|
||||
const hasActivity = workerCount > 0 || visitorCount > 0;
|
||||
const hasActivity = workerCount > 0;
|
||||
|
||||
if (workerCount > 0 && visitorCount > 0) {
|
||||
// 둘 다 있음 - 초록색
|
||||
fillColor = 'rgba(34, 197, 94, 0.3)';
|
||||
strokeColor = 'rgb(34, 197, 94)';
|
||||
} else if (workerCount > 0) {
|
||||
// 내부 작업자만 - 파란색
|
||||
if (workerCount > 0) {
|
||||
// 작업자 있음 - 파란색
|
||||
fillColor = 'rgba(59, 130, 246, 0.3)';
|
||||
strokeColor = 'rgb(59, 130, 246)';
|
||||
} else if (visitorCount > 0) {
|
||||
// 외부 방문자만 - 보라색
|
||||
fillColor = 'rgba(168, 85, 247, 0.3)';
|
||||
strokeColor = 'rgb(168, 85, 247)';
|
||||
} else {
|
||||
// 인원 없음 - 회색 테두리만
|
||||
fillColor = 'rgba(0, 0, 0, 0)'; // 투명
|
||||
@@ -332,9 +280,8 @@ function drawWorkplaceRegion(region, workerCount, visitorCount) {
|
||||
ctx.stroke();
|
||||
|
||||
// 텍스트
|
||||
const totalCount = workerCount + visitorCount;
|
||||
ctx.fillStyle = strokeColor;
|
||||
ctx.fillText(totalCount.toString(), centerX, centerY);
|
||||
ctx.fillText(workerCount.toString(), centerX, centerY);
|
||||
ctx.restore();
|
||||
} else {
|
||||
// 인원이 없을 때는 작업장 이름만 표시
|
||||
@@ -389,7 +336,6 @@ let currentModalWorkplace = null;
|
||||
function showWorkplaceDetail(workplace) {
|
||||
currentModalWorkplace = workplace;
|
||||
const workers = todayWorkers.filter(w => w.workplace_id === workplace.workplace_id);
|
||||
const visitors = todayVisitors.filter(v => v.workplace_id === workplace.workplace_id);
|
||||
|
||||
// 모달 제목
|
||||
document.getElementById('modalWorkplaceName').textContent = workplace.workplace_name;
|
||||
@@ -397,15 +343,16 @@ function showWorkplaceDetail(workplace) {
|
||||
|
||||
// 요약 카드 업데이트
|
||||
const totalWorkers = workers.reduce((sum, w) => sum + (w.member_count || 0), 0);
|
||||
const totalVisitors = visitors.reduce((sum, v) => sum + (v.visitor_count || 0), 0);
|
||||
|
||||
document.getElementById('summaryWorkerCount').textContent = totalWorkers;
|
||||
document.getElementById('summaryVisitorCount').textContent = totalVisitors;
|
||||
const summaryVisitorEl = document.getElementById('summaryVisitorCount');
|
||||
if (summaryVisitorEl) summaryVisitorEl.textContent = '0';
|
||||
document.getElementById('summaryTaskCount').textContent = workers.length;
|
||||
|
||||
// 배지 업데이트
|
||||
document.getElementById('workerCountBadge').textContent = totalWorkers;
|
||||
document.getElementById('visitorCountBadge').textContent = totalVisitors;
|
||||
const visitorBadgeEl = document.getElementById('visitorCountBadge');
|
||||
if (visitorBadgeEl) visitorBadgeEl.textContent = '0';
|
||||
|
||||
// 현황 개요 탭 - 현재 작업 목록
|
||||
renderCurrentTasks(workers);
|
||||
@@ -416,9 +363,6 @@ function showWorkplaceDetail(workplace) {
|
||||
// 작업자 탭
|
||||
renderWorkersTab(workers);
|
||||
|
||||
// 방문자 탭
|
||||
renderVisitorsTab(visitors);
|
||||
|
||||
// 상세 지도 초기화
|
||||
initDetailMap(workplace);
|
||||
|
||||
@@ -529,33 +473,6 @@ function renderWorkersTab(workers) {
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 방문자 탭 렌더링
|
||||
function renderVisitorsTab(visitors) {
|
||||
const container = document.getElementById('externalVisitorsList');
|
||||
|
||||
if (visitors.length === 0) {
|
||||
container.innerHTML = '<p class="empty-message">금일 방문 예정 인원이 없습니다.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
visitors.forEach(visitor => {
|
||||
const statusText = visitor.status === 'training_completed' ? '교육 완료' : '승인됨';
|
||||
|
||||
html += `
|
||||
<div class="visitor-item">
|
||||
<div class="visitor-item-header">
|
||||
<p class="visitor-item-title">${escapeHtml(visitor.visitor_company)}</p>
|
||||
<span class="visitor-item-badge">${parseInt(visitor.visitor_count) || 0}명 • ${statusText}</span>
|
||||
</div>
|
||||
<p class="visitor-item-detail">⏰ ${escapeHtml(visitor.visit_time)}</p>
|
||||
<p class="visitor-item-detail">📋 ${escapeHtml(visitor.purpose_name)}</p>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 상세 지도 초기화
|
||||
async function initDetailMap(workplace) {
|
||||
|
||||
Reference in New Issue
Block a user