화면별 매뉴얼·이 화면 설명 버튼·탭 새로고침/경고·최근방문 기록 보강.

- 매뉴얼: 화면(소메뉴)별 용어·버튼·필드 설명으로 확장 + 기본정보 페이지 신규,
  개요에 용어 사전 추가 (종량제 지식 없는 사용자 대상)
- "이 화면 설명" 버튼: 화면 경로→매뉴얼 매핑(Config\Manual::screenHelp,
  manual_help_url_for_path). 워크스페이스 탭은 새 탭으로, 직접 페이지는 새 창으로
- 워크스페이스: 개별 탭 새로고침(↻) 버튼, 탭 2개 이상일 때만 새로고침 경고,
  사이드바 하단 링크(매뉴얼 등)도 탭으로 열기
- 임베드: 탭 내 링크/폼 embed 유지(중첩 헤더 방지), 매뉴얼 리다이렉트 embed 유지
- 사이드바 하단: 종합그래프 → 사용자 매뉴얼 링크
- 최근 방문 메뉴: embed 페이지에도 방문 기록, 대시보드는 storage 이벤트로 실시간 갱신
- E2E qa_sweep 추가(주요 화면 콘솔/오버레이/매뉴얼/도움말 매핑 점검)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
taekyoungc
2026-06-08 19:04:41 +09:00
parent c15e01bfa7
commit e8d58b5837
15 changed files with 578 additions and 162 deletions

View File

@@ -47,7 +47,8 @@ if ($effectiveLgIdx) {
.ws-tab { display: inline-flex; align-items: center; gap: .4rem; max-width: 200px; padding: .35rem .6rem; background: #f5f7fa; border: 1px solid var(--border); border-bottom: none; border-radius: 7px 7px 0 0; font-size: .78rem; color: #555; cursor: pointer; white-space: nowrap; }
.ws-tab .t-name { overflow: hidden; text-overflow: ellipsis; max-width: 150px; }
.ws-tab.active { background: #fff; color: var(--navy); font-weight: 700; box-shadow: 0 -2px 0 #007bff inset; }
.ws-tab .t-close { width: 16px; height: 16px; line-height: 14px; text-align: center; border-radius: 50%; color: #999; font-size: 12px; }
.ws-tab .t-refresh, .ws-tab .t-close { width: 16px; height: 16px; line-height: 14px; text-align: center; border-radius: 50%; color: #999; font-size: 12px; }
.ws-tab .t-refresh:hover { background: #dbeafe; color: #1d4ed8; }
.ws-tab .t-close:hover { background: #e2e8f0; color: #333; }
.ws-panels { flex: 1; position: relative; min-height: 0; background: #f0f4f8; }
.ws-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: none; background: #f0f4f8; }
@@ -114,6 +115,13 @@ if ($effectiveLgIdx) {
if (empty) empty.style.display = 'none';
}
function reloadTab(id) {
var t = tabs[id];
if (!t) return;
try { t.frame.contentWindow.location.reload(); }
catch (e) { t.frame.src = t.frame.src; } // 교차출처 등 예외 시 src 재설정
}
function closeTab(id) {
if (!tabs[id]) return;
tabs[id].frame.remove();
@@ -142,11 +150,20 @@ if ($effectiveLgIdx) {
var nameSpan = document.createElement('span');
nameSpan.className = 't-name';
nameSpan.textContent = title || '탭';
var refresh = document.createElement('span');
refresh.className = 't-refresh';
refresh.textContent = '↻';
refresh.title = '이 탭 새로고침';
var close = document.createElement('span');
close.className = 't-close';
close.textContent = '×';
btn.appendChild(nameSpan); btn.appendChild(close);
btn.addEventListener('click', function (e) { if (e.target === close) { closeTab(id); } else { activate(id); } });
close.title = '탭 닫기';
btn.appendChild(nameSpan); btn.appendChild(refresh); btn.appendChild(close);
btn.addEventListener('click', function (e) {
if (e.target === close) { closeTab(id); }
else if (e.target === refresh) { activate(id); reloadTab(id); }
else { activate(id); }
});
bar.appendChild(btn);
tabs[id] = { url: url, title: title, frame: frame, btn: btn };
@@ -160,8 +177,8 @@ if ($effectiveLgIdx) {
var a = e.target.closest('a[href]');
if (!a) return;
var href = a.getAttribute('href') || '';
// 외부/특수 링크(로그아웃, 지자체선택 등 사이드바 하단 블록)는 그대로 이동
if (a.closest('.sidebar-blocks')) return;
// 지자체 선택(sb-gray)·모바일앱(sb-teal)은 그대로 이동, 나머지(소메뉴·하단 링크)는 탭으로
if (a.closest('.sb-gray') || a.closest('.sb-teal')) return;
if (/\/logout|\/login/.test(href) || href.charAt(0) === '#' || href === '') return;
e.preventDefault();
openTab(href, (a.textContent || '').trim());
@@ -177,6 +194,15 @@ if ($effectiveLgIdx) {
});
});
// 브라우저 전체 새로고침/이탈 시 경고 (기본 대시보드 외 탭이 열려 있을 때)
window.addEventListener('beforeunload', function (e) {
if (order.length > 1) {
e.preventDefault();
e.returnValue = '열어 둔 탭이 모두 닫힙니다. 계속할까요?';
return e.returnValue;
}
});
// 첫 화면: 대시보드 탭 자동 열기
openTab('<?= base_url('/') ?>', '업무 현황');
})();