feat(tkds): 대시보드 바로가기를 동적 배너로 교체
17개 개별 페이지 바로가기 제거, API 기반 동적 배너(미확인 알림/미제출 TBM/휴가 승인 대기)로 교체. 시스템 카드 7개는 기존 유지. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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>📌</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>🏢</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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user