refactor: 코드 관리 페이지 삭제 및 프론트엔드 모듈화
- codes.html, code-management.js 삭제 (tasks.html에서 동일 기능 제공) - 사이드바에서 코드 관리 링크 제거 - daily-work-report, tbm, workplace-management JS 모듈 분리 - common/security.js 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
253
web-ui/js/tbm/utils.js
Normal file
253
web-ui/js/tbm/utils.js
Normal file
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* TBM - Utilities
|
||||
* TBM 관련 유틸리티 함수들
|
||||
*/
|
||||
|
||||
class TbmUtils {
|
||||
constructor() {
|
||||
console.log('[TbmUtils] 초기화 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
* 서울 시간대(Asia/Seoul, UTC+9) 기준 오늘 날짜를 YYYY-MM-DD 형식으로 반환
|
||||
*/
|
||||
getTodayKST() {
|
||||
const now = new Date();
|
||||
const kstOffset = 9 * 60;
|
||||
const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
|
||||
const kstTime = new Date(utc + (kstOffset * 60000));
|
||||
|
||||
const year = kstTime.getFullYear();
|
||||
const month = String(kstTime.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(kstTime.getDate()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* ISO 날짜 문자열을 YYYY-MM-DD 형식으로 변환
|
||||
*/
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||
return dateString;
|
||||
}
|
||||
|
||||
const date = new Date(dateString);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 날짜 표시용 포맷 (MM월 DD일)
|
||||
*/
|
||||
formatDateDisplay(dateString) {
|
||||
if (!dateString) return '';
|
||||
const [year, month, day] = dateString.split('-');
|
||||
return `${parseInt(month)}월 ${parseInt(day)}일`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 날짜를 연/월/일/요일 형식으로 포맷
|
||||
*/
|
||||
formatDateFull(dateString) {
|
||||
if (!dateString) return '';
|
||||
const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
|
||||
const [year, month, day] = dateString.split('-');
|
||||
const dateObj = new Date(dateString);
|
||||
const dayName = dayNames[dateObj.getDay()];
|
||||
return `${year}년 ${parseInt(month)}월 ${parseInt(day)}일 (${dayName})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 요일 반환
|
||||
*/
|
||||
getDayOfWeek(dateString) {
|
||||
const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
|
||||
const dateObj = new Date(dateString + 'T00:00:00');
|
||||
return dayNames[dateObj.getDay()];
|
||||
}
|
||||
|
||||
/**
|
||||
* 오늘인지 확인
|
||||
*/
|
||||
isToday(dateString) {
|
||||
const today = this.getTodayKST();
|
||||
return this.formatDate(dateString) === today;
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 시간을 HH:MM 형식으로 반환
|
||||
*/
|
||||
getCurrentTime() {
|
||||
return new Date().toTimeString().slice(0, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID 생성
|
||||
*/
|
||||
generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML 이스케이프
|
||||
*/
|
||||
escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* 날씨 조건명 반환
|
||||
*/
|
||||
getWeatherConditionName(code) {
|
||||
const names = {
|
||||
clear: '맑음',
|
||||
rain: '비',
|
||||
snow: '눈',
|
||||
heat: '폭염',
|
||||
cold: '한파',
|
||||
wind: '강풍',
|
||||
fog: '안개',
|
||||
dust: '미세먼지'
|
||||
};
|
||||
return names[code] || code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 날씨 아이콘 반환
|
||||
*/
|
||||
getWeatherIcon(code) {
|
||||
const icons = {
|
||||
clear: '☀️',
|
||||
rain: '🌧️',
|
||||
snow: '❄️',
|
||||
heat: '🔥',
|
||||
cold: '🥶',
|
||||
wind: '💨',
|
||||
fog: '🌫️',
|
||||
dust: '😷'
|
||||
};
|
||||
return icons[code] || '🌤️';
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리명 반환
|
||||
*/
|
||||
getCategoryName(category) {
|
||||
const names = {
|
||||
'PPE': '개인 보호 장비',
|
||||
'EQUIPMENT': '장비 점검',
|
||||
'ENVIRONMENT': '작업 환경',
|
||||
'EMERGENCY': '비상 대응',
|
||||
'WEATHER': '날씨',
|
||||
'TASK': '작업'
|
||||
};
|
||||
return names[category] || category;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 배지 HTML 반환
|
||||
*/
|
||||
getStatusBadge(status) {
|
||||
const badges = {
|
||||
'draft': '<span class="tbm-card-status draft">진행중</span>',
|
||||
'completed': '<span class="tbm-card-status completed">완료</span>',
|
||||
'cancelled': '<span class="tbm-card-status cancelled">취소</span>'
|
||||
};
|
||||
return badges[status] || '';
|
||||
}
|
||||
}
|
||||
|
||||
// 전역 인스턴스 생성
|
||||
window.TbmUtils = new TbmUtils();
|
||||
|
||||
// 하위 호환성: 기존 함수들
|
||||
window.getTodayKST = () => window.TbmUtils.getTodayKST();
|
||||
window.formatDate = (dateString) => window.TbmUtils.formatDate(dateString);
|
||||
|
||||
// 토스트 알림
|
||||
window.showToast = function(message, type = 'info', duration = 3000) {
|
||||
const container = document.getElementById('toastContainer');
|
||||
if (!container) {
|
||||
console.log(`[Toast] ${type}: ${message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type}`;
|
||||
|
||||
const iconMap = {
|
||||
success: '✅',
|
||||
error: '❌',
|
||||
warning: '⚠️',
|
||||
info: 'ℹ️'
|
||||
};
|
||||
|
||||
toast.innerHTML = `
|
||||
<div class="toast-icon">${iconMap[type] || 'ℹ️'}</div>
|
||||
<div class="toast-message">${message}</div>
|
||||
<button class="toast-close" onclick="this.parentElement.remove()">×</button>
|
||||
`;
|
||||
|
||||
toast.style.cssText = `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.25rem;
|
||||
background: white;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
||||
margin-bottom: 0.75rem;
|
||||
min-width: 300px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
`;
|
||||
|
||||
container.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
if (toast.parentElement) {
|
||||
toast.style.animation = 'slideOut 0.3s ease-out';
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}
|
||||
}, duration);
|
||||
};
|
||||
|
||||
// 카테고리별 그룹화
|
||||
window.groupChecksByCategory = function(checks) {
|
||||
return checks.reduce((acc, check) => {
|
||||
const category = check.check_category || 'OTHER';
|
||||
if (!acc[category]) acc[category] = [];
|
||||
acc[category].push(check);
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
// 작업별 그룹화
|
||||
window.groupChecksByTask = function(checks) {
|
||||
return checks.reduce((acc, check) => {
|
||||
const taskId = check.task_id || 0;
|
||||
const taskName = check.task_name || '기타 작업';
|
||||
if (!acc[taskId]) acc[taskId] = { name: taskName, items: [] };
|
||||
acc[taskId].items.push(check);
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
// Admin 사용자 확인
|
||||
window.isAdminUser = function() {
|
||||
return window.TbmState?.isAdminUser() || false;
|
||||
};
|
||||
|
||||
console.log('[Module] tbm/utils.js 로드 완료');
|
||||
Reference in New Issue
Block a user