feat(tkds): 대시보드 바로가기를 동적 배너로 교체

17개 개별 페이지 바로가기 제거, API 기반 동적 배너(미확인 알림/미제출 TBM/휴가 승인 대기)로 교체.
시스템 카드 7개는 기존 유지.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-13 18:51:31 +09:00
parent ccdb1087d7
commit a66656b1c3

View File

@@ -193,17 +193,32 @@
.system-card .card-icon { font-size: 26px; }
.system-card .card-name { font-size: 14px; font-weight: 600; }
/* Shortcut category labels */
.shortcut-cat-label {
grid-column: 1 / -1;
font-size: 12px;
font-weight: 600;
color: #6b7280;
padding: 8px 0 2px;
border-bottom: 1px solid #e5e7eb;
margin-bottom: 4px;
/* Banner */
.banner-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.shortcut-cat-label:first-child { padding-top: 0; }
.banner-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 16px;
background: white;
border-radius: 10px;
border-left: 4px solid;
text-decoration: none;
color: inherit;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
transition: transform 0.15s, box-shadow 0.15s;
}
.banner-item:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.banner-icon { font-size: 20px; flex-shrink: 0; }
.banner-text { font-size: 14px; font-weight: 500; color: #1f2937; flex: 1; }
.banner-arrow { font-size: 18px; color: #9ca3af; }
/* Coming soon */
.badge-soon {
@@ -265,9 +280,8 @@
</div>
</div>
<div class="container">
<div class="section" id="shortcutSection" style="display:none">
<div class="section-title"><span>&#128204;</span> 내 바로가기</div>
<div class="card-grid" id="shortcutGrid"></div>
<div class="section" id="bannerSection" style="display:none">
<div class="banner-list" id="bannerList"></div>
</div>
<div class="section" id="systemSection" style="display:none">
<div class="section-title"><span>&#127970;</span> 시스템</div>
@@ -334,36 +348,90 @@
return protocol + '//' + hostname + ':' + (ports[name] || 30000);
}
// ===== Card Definitions =====
var ALL_SHORTCUTS = [
// 작업 관리
{ id: 'tbm', name: 'TBM', icon: '\uD83D\uDCCB', cat: '작업 관리', subdomain: 'tkfb', path: '/pages/work/tbm.html', pageKey: 's1.work.tbm' },
{ id: 'report', name: '작업보고서', icon: '\uD83D\uDCDD', cat: '작업 관리', subdomain: 'tkfb', path: '/pages/work/report-create.html', pageKey: 's1.work.report_create' },
{ id: 'analysis', name: '작업 분석', icon: '\uD83D\uDCCA', cat: '작업 관리', subdomain: 'tkfb', path: '/pages/work/analysis.html', pageKey: 's1.work.analysis' },
{ id: 'nonconformity', name: '부적합 현황', icon: '\u26A0\uFE0F', cat: '작업 관리', subdomain: 'tkfb', path: '/pages/work/nonconformity.html', pageKey: 's1.work.nonconformity' },
// 공장 관리
{ id: 'repair', name: '시설설비 관리', icon: '\uD83D\uDD27', cat: '공장 관리', subdomain: 'tkfb', path: '/pages/admin/repair-management.html', pageKey: 's1.factory.repair_management' },
{ id: 'patrol', name: '일일순회점검', icon: '\uD83D\uDD0D', cat: '공장 관리', subdomain: 'tkfb', path: '/pages/inspection/daily-patrol.html', pageKey: 's1.inspection.daily_patrol' },
{ id: 'checkin', name: '출퇴근 체크', icon: '\u23F0', cat: '공장 관리', subdomain: 'tkfb', path: '/pages/attendance/checkin.html', pageKey: 's1.inspection.checkin' },
{ id: 'workstatus', name: '근무 현황', icon: '\uD83D\uDCBC', cat: '공장 관리', subdomain: 'tkfb', path: '/pages/attendance/work-status.html', pageKey: 's1.inspection.work_status' },
// 근태 관리
{ id: 'vacation', name: '내 연차 정보', icon: '\uD83C\uDFD6\uFE0F', cat: '근태 관리', subdomain: 'tkfb', path: '/pages/attendance/my-vacation-info.html', pageKey: 's1.attendance.my_vacation_info' },
{ id: 'monthly', name: '월간 근태', icon: '\uD83D\uDCC6', cat: '근태 관리', subdomain: 'tkfb', path: '/pages/attendance/monthly.html', pageKey: 's1.attendance.monthly' },
{ id: 'leave', name: '휴가 신청', icon: '\uD83D\uDCC5', cat: '근태 관리', subdomain: 'tkfb', path: '/pages/attendance/vacation-request.html', pageKey: 's1.attendance.vacation_request' },
{ id: 'vacmgmt', name: '휴가 관리', icon: '\u2699\uFE0F', cat: '근태 관리', subdomain: 'tkfb', path: '/pages/attendance/vacation-management.html', pageKey: 's1.attendance.vacation_management' },
// 신고·안전
{ id: 'issue', name: '안전신고', icon: '\uD83D\uDEA8', cat: '신고·안전', subdomain: 'tkreport', path: '/pages/safety/issue-report.html', accessKey: 'system2' },
{ id: 'visit', name: '방문 관리', icon: '\uD83D\uDEAA', cat: '신고·안전', subdomain: 'tksafety', pageKey: 'safety_visit_management' },
{ id: 'education', name: '안전교육', icon: '\uD83C\uDF93', cat: '신고·안전', subdomain: 'tksafety', path: '/education.html', pageKey: 'safety_visit_management' },
// 구매·행정
{ id: 'daylabor', name: '일용공 신청', icon: '\uD83D\uDC77', cat: '구매·행정', subdomain: 'tkpurchase', path: '/daylabor.html', pageKey: 'purchasing_schedule' },
{ id: 'schedule', name: '작업일정', icon: '\uD83D\uDCC5', cat: '구매·행정', subdomain: 'tkpurchase', path: '/schedule.html', pageKey: 'purchasing_schedule' }
// ===== Banner Definitions =====
var BANNERS = [
{
id: 'notifications',
icon: '\uD83D\uDD14',
api: '/api/notifications/unread/count',
parse: function(data) {
var count = data.data && data.data.count;
return count > 0 ? { text: '\uBBF8\uD655\uC778 \uC54C\uB9BC ' + count + '\uAC74' } : null;
},
subdomain: 'tkfb',
path: '/pages/profile/notifications.html',
color: '#1a56db'
},
{
id: 'tbm',
icon: '\uD83D\uDCCB',
api: '/api/tbm/sessions/incomplete-reports',
parse: function(data) {
var items = data.data || data;
var count = Array.isArray(items) ? items.length : 0;
return count > 0 ? { text: '\uBBF8\uC81C\uCD9C TBM \uBCF4\uACE0 ' + count + '\uAC74' } : null;
},
subdomain: 'tkfb',
path: '/pages/work/tbm.html',
color: '#d97706',
requirePageKey: 's1.work.tbm'
},
{
id: 'vacation',
icon: '\uD83D\uDCC5',
api: '/api/vacation-requests/pending',
parse: function(data) {
var items = data.data || data;
var count = Array.isArray(items) ? items.length : 0;
return count > 0 ? { text: '\uD734\uAC00 \uC2B9\uC778 \uB300\uAE30 ' + count + '\uAC74' } : null;
},
subdomain: 'tkfb',
path: '/pages/attendance/vacation-management.html',
color: '#7c3aed',
requirePageKey: 's1.attendance.vacation_management'
}
];
// ===== Banner Loading =====
async function loadBanners(token, allowed) {
var container = document.getElementById('bannerList');
container.innerHTML = '';
var visible = BANNERS.filter(function(b) {
return !b.requirePageKey || allowed.has(b.requirePageKey);
});
var results = await Promise.allSettled(
visible.map(function(b) {
return fetch(b.api, { headers: { 'Authorization': 'Bearer ' + token } })
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) { return data ? { banner: b, result: b.parse(data) } : null; });
})
);
var anyVisible = false;
results.forEach(function(r) {
if (r.status !== 'fulfilled' || !r.value || !r.value.result) return;
var b = r.value.banner;
var result = r.value.result;
var a = document.createElement('a');
a.className = 'banner-item';
a.style.borderLeftColor = b.color;
a.href = getSubdomainUrl(b.subdomain) + (b.path || '');
a.innerHTML = '<span class="banner-icon">' + b.icon + '</span>'
+ '<span class="banner-text">' + result.text + '</span>'
+ '<span class="banner-arrow">\u203A</span>';
container.appendChild(a);
anyVisible = true;
});
if (anyVisible) {
document.getElementById('bannerSection').style.display = '';
}
}
// ===== Card Definitions =====
var SYSTEM_CARDS = [
{ id: 'factory', name: '공장관리', icon: '\uD83C\uDFED', subdomain: 'tkfb', path: '/pages/dashboard.html', pageKey: 's1.dashboard', color: '#1a56db' },
{ id: 'report_sys', name: '신고', icon: '\uD83D\uDEA8', subdomain: 'tkreport', accessKey: 'system2', color: '#dc2626' },
@@ -434,30 +502,9 @@
var grid = document.getElementById(gridId);
grid.innerHTML = '';
if (!isSystem && visible.length >= 10) {
// 카테고리별 그룹핑
var categories = [];
var catMap = {};
visible.forEach(function(c) {
var cat = c.cat || '기타';
if (!catMap[cat]) { catMap[cat] = []; categories.push(cat); }
catMap[cat].push(c);
});
categories.forEach(function(cat) {
var label = document.createElement('div');
label.className = 'shortcut-cat-label';
label.textContent = cat;
grid.appendChild(label);
catMap[cat].forEach(function(card) {
grid.appendChild(createCardElement(card, false));
});
});
} else {
visible.forEach(function(card) {
grid.appendChild(createCardElement(card, isSystem));
});
}
visible.forEach(function(card) {
grid.appendChild(createCardElement(card, isSystem));
});
document.getElementById(sectionId).style.display = '';
}
@@ -494,8 +541,8 @@
if (systemAccess.system3 !== false) allowed.add('issues_dashboard');
}
// Render sections
renderSection('shortcutSection', 'shortcutGrid', ALL_SHORTCUTS, allowed, systemAccess, false);
// Render banners + system cards
loadBanners(token, allowed);
renderSection('systemSection', 'systemGrid', SYSTEM_CARDS, allowed, systemAccess, true);
}