- itsm/ -> workspace/guardia-itsm/ - manager/ -> workspace/guardia-manager/ - app/ -> workspace/guardia-messenger/ - manual/ -> workspace/guardia-docs/ workspace/zioinfo-web/ unchanged. git mv preserves full commit history. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
451 lines
19 KiB
JavaScript
451 lines
19 KiB
JavaScript
/**
|
|
* GUARDiA ITSM 화면별 도움말 시스템 (GS인증 사용성 요구사항)
|
|
* - 각 화면/기능의 ? 버튼 → 팝업 도움말
|
|
* - F1 키 → 현재 화면 도움말
|
|
* - 검색 가능한 도움말 DB
|
|
*/
|
|
(function GUARDiAHelp() {
|
|
'use strict';
|
|
|
|
// ── 도움말 데이터베이스 ────────────────────────────────────
|
|
const HELP_DB = {
|
|
'dashboard': {
|
|
title: '대시보드',
|
|
icon: '📊',
|
|
content: `
|
|
<h3>통합 대시보드</h3>
|
|
<p>GUARDiA ITSM의 모든 운영 현황을 한눈에 확인할 수 있는 화면입니다.</p>
|
|
<h4>탭 구성</h4>
|
|
<ul>
|
|
<li><strong>운영 현황</strong>: SR 상태·SLA·7일 추이 차트</li>
|
|
<li><strong>인프라</strong>: 서버 헬스·기관별 현황</li>
|
|
<li><strong>보안</strong>: 취약점·패치 현황</li>
|
|
<li><strong>AI 인사이트</strong>: 이상탐지·예측 현황</li>
|
|
</ul>
|
|
<h4>KPI 카드</h4>
|
|
<p>상단 숫자 카드를 클릭하면 해당 상세 목록으로 이동합니다.</p>
|
|
<h4>단축키</h4>
|
|
<ul><li>F5: 새로고침</li><li>F1: 이 도움말</li></ul>
|
|
`,
|
|
},
|
|
'tasks': {
|
|
title: 'SR 서비스 요청',
|
|
icon: '📋',
|
|
content: `
|
|
<h3>SR 서비스 요청 관리</h3>
|
|
<p>IT 서비스 요청(SR)을 접수·처리·추적하는 화면입니다.</p>
|
|
<h4>SR 상태 흐름</h4>
|
|
<div class="help-flow">
|
|
접수 → 파싱 → 승인대기 → 승인 → 진행중 → PM검증 → 완료
|
|
</div>
|
|
<h4>주요 기능</h4>
|
|
<ul>
|
|
<li><strong>AI 자동 분류</strong>: SR 생성 시 우선순위·카테고리 자동 제안</li>
|
|
<li><strong>SLA 타이머</strong>: 우선순위별 처리 기한 자동 계산</li>
|
|
<li><strong>대량 처리</strong>: 여러 SR을 한 번에 상태 변경</li>
|
|
</ul>
|
|
<h4>봇 명령어</h4>
|
|
<code>/sr <제목></code> - 메신저에서 즉시 접수<br>
|
|
<code>/sla</code> - SLA 위반 목록 조회
|
|
`,
|
|
},
|
|
'cmdb': {
|
|
title: 'CMDB 형상관리',
|
|
icon: '🖥️',
|
|
content: `
|
|
<h3>CMDB (Configuration Management Database)</h3>
|
|
<p>관리하는 모든 IT 자산(서버·소프트웨어·네트워크)을 등록·관리합니다.</p>
|
|
<h4>서버 등록 방법</h4>
|
|
<ol>
|
|
<li>서버 관리 → 서버 등록 버튼 클릭</li>
|
|
<li>서버명, IP, OS, SSH 계정 입력</li>
|
|
<li>SSH 비밀번호는 AES-256 암호화 저장</li>
|
|
</ol>
|
|
<h4>CI 의존관계</h4>
|
|
<p>서버 간 의존관계를 등록하면 배포 영향도 자동 분석에 활용됩니다.</p>
|
|
<h4>보안 주의사항</h4>
|
|
<p>⚠️ root 계정 SSH 직접 접속 금지 — opsagent 계정 사용</p>
|
|
`,
|
|
},
|
|
'incidents': {
|
|
title: '인시던트 관리',
|
|
icon: '🚨',
|
|
content: `
|
|
<h3>인시던트(장애) 관리</h3>
|
|
<p>IT 서비스 장애를 신속하게 탐지·대응·복구하는 프로세스입니다.</p>
|
|
<h4>장애 등급</h4>
|
|
<ul>
|
|
<li><strong>P1 🚨</strong>: 전체 서비스 중단 — 즉시 대응, MTTR 1시간</li>
|
|
<li><strong>P2 🔴</strong>: 주요 기능 장애 — MTTR 4시간</li>
|
|
<li><strong>P3 🟠</strong>: 부분 영향 — MTTR 24시간</li>
|
|
<li><strong>P4 🟡</strong>: 경미 — MTTR 72시간</li>
|
|
</ul>
|
|
<h4>AI 자동 RCA</h4>
|
|
<p>인시던트 종료 시 Ollama AI가 근본원인 초안을 자동 생성합니다.</p>
|
|
<h4>봇 명령어</h4>
|
|
<code>/incident <제목> P1</code> - 즉시 P1 인시던트 등록<br>
|
|
<code>/rca INC-XXXX</code> - AI RCA 분석 요청
|
|
`,
|
|
},
|
|
'si': {
|
|
title: 'PMS 프로젝트 관리',
|
|
icon: '🏗️',
|
|
content: `
|
|
<h3>PMS (Project Management System)</h3>
|
|
<p>SI 프로젝트의 전체 생명주기를 관리합니다.</p>
|
|
<h4>관리 항목</h4>
|
|
<ul>
|
|
<li><strong>WBS</strong>: 작업 분류 체계, 진척률 입력</li>
|
|
<li><strong>산출물</strong>: 문서 제출·검토·승인 워크플로우</li>
|
|
<li><strong>이슈</strong>: 프로젝트 이슈 → SR 자동 연결</li>
|
|
<li><strong>위험</strong>: 리스크 매트릭스 관리</li>
|
|
<li><strong>보고서</strong>: 일간/주간/월간 자동 생성</li>
|
|
</ul>
|
|
<h4>자동 보고서</h4>
|
|
<p>매일 18:00 일일 보고서, 매주 금요일 주간 보고서가 운영팀에 자동 발송됩니다.</p>
|
|
`,
|
|
},
|
|
'license': {
|
|
title: '라이선스 관리',
|
|
icon: '🔏',
|
|
content: `
|
|
<h3>라이선스 관리</h3>
|
|
<h4>에디션 비교</h4>
|
|
<table class="help-table">
|
|
<tr><th>에디션</th><th>기관</th><th>사용자</th><th>기능</th></tr>
|
|
<tr><td>COMMUNITY</td><td>1</td><td>10</td><td>기본</td></tr>
|
|
<tr><td>STANDARD</td><td>50</td><td>200</td><td>전체</td></tr>
|
|
<tr><td>ENTERPRISE</td><td>무제한</td><td>무제한</td><td>전체+APM</td></tr>
|
|
</table>
|
|
<h4>체험판</h4>
|
|
<p>무료 체험 시작 버튼으로 30일 체험판을 즉시 활성화할 수 있습니다.</p>
|
|
<h4>라이선스 갱신</h4>
|
|
<p>만료 30일 전부터 알림이 발송됩니다. 갱신 키를 입력하여 연장하세요.</p>
|
|
`,
|
|
},
|
|
'agents': {
|
|
title: 'AI 에이전트',
|
|
icon: '🤖',
|
|
content: `
|
|
<h3>AI 에이전트 시스템</h3>
|
|
<p>GUARDiA의 AI 에이전트는 온프레미스 Ollama LLM을 사용합니다. 외부 API 호출 없음.</p>
|
|
<h4>에이전트 역할</h4>
|
|
<ul>
|
|
<li><strong>SR 매니저</strong>: SR 자동 분류·배정</li>
|
|
<li><strong>코드 리뷰어</strong>: 배포 전 코드 품질 검토</li>
|
|
<li><strong>SLA 가디언</strong>: SLA 위반 모니터링·에스컬레이션</li>
|
|
<li><strong>KB 큐레이터</strong>: 해결된 SR → KB 자동 생성</li>
|
|
</ul>
|
|
<h4>Ollama 상태 확인</h4>
|
|
<p>상단 Ollama 상태 표시가 🟢이면 AI 기능 사용 가능합니다.</p>
|
|
`,
|
|
},
|
|
'default': {
|
|
title: 'GUARDiA ITSM 도움말',
|
|
icon: '❓',
|
|
content: `
|
|
<h3>GUARDiA ITSM v2.0</h3>
|
|
<p>AI 기반 레거시 인프라 자율 운영 플랫폼</p>
|
|
<h4>빠른 시작</h4>
|
|
<ul>
|
|
<li>좌측 메뉴에서 원하는 기능을 선택하세요</li>
|
|
<li>각 화면에서 <kbd>?</kbd> 버튼을 누르면 상세 도움말이 표시됩니다</li>
|
|
<li><kbd>F1</kbd>로 언제든 도움말을 열 수 있습니다</li>
|
|
</ul>
|
|
<h4>메신저 봇 명령어</h4>
|
|
<ul>
|
|
<li><code>/help</code> - 전체 명령어 목록</li>
|
|
<li><code>/sr <제목></code> - SR 접수</li>
|
|
<li><code>/status</code> - 시스템 현황</li>
|
|
</ul>
|
|
<h4>기술 지원</h4>
|
|
<p>📧 support@zioinfo.co.kr | 📞 02-000-0000</p>
|
|
`,
|
|
},
|
|
};
|
|
|
|
// ── 현재 뷰 감지 ───────────────────────────────────────────
|
|
function getCurrentView() {
|
|
const path = location.pathname;
|
|
const viewEl = document.querySelector('[data-view].active');
|
|
const viewId = viewEl?.dataset?.view;
|
|
|
|
if (viewId) return viewId;
|
|
if (path.includes('/si')) return 'si';
|
|
if (path.includes('/incidents')) return 'incidents';
|
|
if (path.includes('/license')) return 'license';
|
|
if (path.includes('/agents')) return 'agents';
|
|
return 'default';
|
|
}
|
|
|
|
// ── 팝업 HTML 빌드 ─────────────────────────────────────────
|
|
function buildPopup() {
|
|
if (document.getElementById('grd-help-popup')) return;
|
|
|
|
const overlay = document.createElement('div');
|
|
overlay.id = 'grd-help-overlay';
|
|
overlay.innerHTML = `
|
|
<div id="grd-help-popup" role="dialog" aria-modal="true" aria-labelledby="grd-help-title">
|
|
<div id="grd-help-header">
|
|
<span id="grd-help-icon">❓</span>
|
|
<h2 id="grd-help-title">도움말</h2>
|
|
<button id="grd-help-close" aria-label="도움말 닫기" title="닫기 (ESC)">✕</button>
|
|
</div>
|
|
<div id="grd-help-search-area">
|
|
<input id="grd-help-search" type="search" placeholder="도움말 검색..." aria-label="도움말 검색">
|
|
</div>
|
|
<div id="grd-help-body" role="document"></div>
|
|
<div id="grd-help-nav">
|
|
<button class="grd-help-topic" data-topic="dashboard">📊 대시보드</button>
|
|
<button class="grd-help-topic" data-topic="tasks">📋 SR 관리</button>
|
|
<button class="grd-help-topic" data-topic="cmdb">🖥️ CMDB</button>
|
|
<button class="grd-help-topic" data-topic="incidents">🚨 인시던트</button>
|
|
<button class="grd-help-topic" data-topic="si">🏗️ PMS</button>
|
|
<button class="grd-help-topic" data-topic="license">🔏 라이선스</button>
|
|
<button class="grd-help-topic" data-topic="agents">🤖 AI 에이전트</button>
|
|
</div>
|
|
<div id="grd-help-footer">
|
|
<span>GUARDiA ITSM v2.0 | Copyright © 2026 (주)지오정보기술</span>
|
|
<a href="mailto:support@zioinfo.co.kr">기술 지원</a>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const style = document.createElement('style');
|
|
style.id = 'grd-help-style';
|
|
style.textContent = `
|
|
#grd-help-overlay {
|
|
position: fixed; inset: 0; z-index: 10000;
|
|
background: rgba(0,0,0,.6); backdrop-filter: blur(4px);
|
|
display: flex; align-items: center; justify-content: center;
|
|
animation: fadeIn .15s ease;
|
|
}
|
|
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
|
|
#grd-help-popup {
|
|
background: var(--surface, #1e2333);
|
|
border: 1px solid var(--border, rgba(255,255,255,.1));
|
|
border-radius: 16px;
|
|
width: min(680px, 95vw);
|
|
max-height: 80vh;
|
|
display: flex; flex-direction: column;
|
|
box-shadow: 0 24px 80px rgba(0,0,0,.5);
|
|
animation: slideUp .2s ease;
|
|
}
|
|
@keyframes slideUp { from{transform:translateY(20px);opacity:0} to{transform:translateY(0);opacity:1} }
|
|
#grd-help-header {
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 18px 20px; border-bottom: 1px solid var(--border, rgba(255,255,255,.08));
|
|
flex-shrink: 0;
|
|
}
|
|
#grd-help-icon { font-size: 24px; }
|
|
#grd-help-title {
|
|
flex: 1; font-size: 18px; font-weight: 700;
|
|
color: var(--text-bright, #f1f5f9); margin: 0;
|
|
}
|
|
#grd-help-close {
|
|
width: 32px; height: 32px; border-radius: 8px;
|
|
background: rgba(255,255,255,.08); border: none;
|
|
color: var(--text-muted, #64748b); cursor: pointer;
|
|
font-size: 16px; display: flex; align-items: center; justify-content: center;
|
|
transition: all .15s;
|
|
}
|
|
#grd-help-close:hover { background: rgba(255,255,255,.15); color: #fff; }
|
|
#grd-help-search-area {
|
|
padding: 12px 20px; border-bottom: 1px solid var(--border, rgba(255,255,255,.08));
|
|
flex-shrink: 0;
|
|
}
|
|
#grd-help-search {
|
|
width: 100%; padding: 8px 14px;
|
|
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1);
|
|
border-radius: 8px; color: var(--text-bright, #f1f5f9);
|
|
font-size: 14px; outline: none; box-sizing: border-box;
|
|
}
|
|
#grd-help-search:focus { border-color: #818cf8; }
|
|
#grd-help-body {
|
|
flex: 1; overflow-y: auto; padding: 20px;
|
|
color: var(--text-bright, #e2e8f0); line-height: 1.7; font-size: 14px;
|
|
scrollbar-width: thin;
|
|
}
|
|
#grd-help-body h3 { font-size: 18px; color: #818cf8; margin: 0 0 12px; }
|
|
#grd-help-body h4 { font-size: 14px; font-weight: 700; color: #a5b4fc; margin: 16px 0 6px; }
|
|
#grd-help-body ul,ol { padding-left: 20px; margin: 6px 0; }
|
|
#grd-help-body li { margin: 4px 0; }
|
|
#grd-help-body code {
|
|
background: rgba(255,255,255,.1); padding: 2px 6px;
|
|
border-radius: 4px; font-family: monospace; font-size: 13px;
|
|
}
|
|
#grd-help-body kbd {
|
|
background: rgba(255,255,255,.15); padding: 1px 6px;
|
|
border-radius: 4px; font-size: 12px; border: 1px solid rgba(255,255,255,.2);
|
|
}
|
|
.help-flow {
|
|
background: rgba(129,140,248,.1); border-left: 3px solid #818cf8;
|
|
padding: 10px 14px; border-radius: 0 8px 8px 0; margin: 8px 0;
|
|
font-family: monospace; font-size: 13px;
|
|
}
|
|
.help-table { border-collapse: collapse; width: 100%; margin: 8px 0; font-size: 13px; }
|
|
.help-table th { background: rgba(255,255,255,.08); padding: 6px 10px; text-align: left; }
|
|
.help-table td { padding: 5px 10px; border-bottom: 1px solid rgba(255,255,255,.05); }
|
|
#grd-help-nav {
|
|
display: flex; flex-wrap: wrap; gap: 6px;
|
|
padding: 12px 20px; border-top: 1px solid rgba(255,255,255,.08);
|
|
flex-shrink: 0;
|
|
}
|
|
.grd-help-topic {
|
|
padding: 5px 12px; border-radius: 20px; font-size: 12px;
|
|
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1);
|
|
color: var(--text-muted, #64748b); cursor: pointer; transition: all .15s;
|
|
}
|
|
.grd-help-topic:hover, .grd-help-topic.active {
|
|
background: rgba(129,140,248,.2); border-color: #818cf8; color: #818cf8;
|
|
}
|
|
#grd-help-footer {
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
padding: 10px 20px; border-top: 1px solid rgba(255,255,255,.05);
|
|
font-size: 11px; color: var(--text-muted, #64748b); flex-shrink: 0;
|
|
}
|
|
#grd-help-footer a { color: #818cf8; }
|
|
/* 화면별 ? 버튼 */
|
|
.grd-help-btn {
|
|
width: 28px; height: 28px; border-radius: 50%;
|
|
background: rgba(129,140,248,.15); border: 1px solid rgba(129,140,248,.3);
|
|
color: #818cf8; cursor: pointer; font-size: 14px; font-weight: 700;
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
transition: all .15s; line-height: 1;
|
|
}
|
|
.grd-help-btn:hover { background: rgba(129,140,248,.3); transform: scale(1.1); }
|
|
`;
|
|
|
|
document.head.appendChild(style);
|
|
document.body.appendChild(overlay);
|
|
|
|
// 이벤트
|
|
overlay.addEventListener('click', e => { if (e.target === overlay) closeHelp(); });
|
|
document.getElementById('grd-help-close').onclick = closeHelp;
|
|
document.getElementById('grd-help-search').oninput = searchHelp;
|
|
document.querySelectorAll('.grd-help-topic').forEach(btn => {
|
|
btn.onclick = () => showTopic(btn.dataset.topic);
|
|
});
|
|
}
|
|
|
|
// ── 도움말 표시 ────────────────────────────────────────────
|
|
function showHelp(topicId) {
|
|
buildPopup();
|
|
const topic = topicId || getCurrentView();
|
|
showTopic(topic);
|
|
document.getElementById('grd-help-overlay').style.display = 'flex';
|
|
document.getElementById('grd-help-search').focus();
|
|
}
|
|
|
|
function showTopic(topicId) {
|
|
const data = HELP_DB[topicId] || HELP_DB['default'];
|
|
document.getElementById('grd-help-icon').textContent = data.icon;
|
|
document.getElementById('grd-help-title').textContent = data.title;
|
|
document.getElementById('grd-help-body').innerHTML = data.content;
|
|
|
|
document.querySelectorAll('.grd-help-topic').forEach(b =>
|
|
b.classList.toggle('active', b.dataset.topic === topicId));
|
|
}
|
|
|
|
function closeHelp() {
|
|
const ol = document.getElementById('grd-help-overlay');
|
|
if (ol) { ol.style.display = 'none'; }
|
|
document.getElementById('grd-help-search').value = '';
|
|
}
|
|
|
|
function searchHelp() {
|
|
const q = this.value.toLowerCase();
|
|
if (!q) { showTopic(getCurrentView()); return; }
|
|
|
|
let results = '';
|
|
for (const [id, data] of Object.entries(HELP_DB)) {
|
|
if (id === 'default') continue;
|
|
const text = data.content.replace(/<[^>]+>/g, '').toLowerCase();
|
|
if (text.includes(q) || data.title.toLowerCase().includes(q)) {
|
|
results += `<div style="margin-bottom:16px">
|
|
<h4 style="cursor:pointer;color:#818cf8" onclick="document.getElementById('grd-help-search').value='';GUARDiAHelp.show('${id}')">${data.icon} ${data.title}</h4>
|
|
<p style="font-size:13px;color:#94a3b8">${data.content.replace(/<[^>]+>/g,'').substring(0,150)}...</p>
|
|
</div>`;
|
|
}
|
|
}
|
|
document.getElementById('grd-help-body').innerHTML =
|
|
results || `<p style="color:#64748b">"${q}"에 대한 결과가 없습니다.</p>`;
|
|
}
|
|
|
|
// ── ? 버튼 자동 삽입 ───────────────────────────────────────
|
|
function injectHelpButtons() {
|
|
const targets = [
|
|
{ selector: '.card-header', topic: null },
|
|
{ selector: '.section-header', topic: null },
|
|
{ selector: '.page-hero-title', topic: null },
|
|
{ selector: '#grd-ob-header', topic: 'default', skip: true },
|
|
];
|
|
|
|
targets.forEach(({ selector, topic, skip }) => {
|
|
if (skip) return;
|
|
document.querySelectorAll(selector).forEach(el => {
|
|
if (el.querySelector('.grd-help-btn')) return;
|
|
const btn = document.createElement('button');
|
|
btn.className = 'grd-help-btn';
|
|
btn.textContent = '?';
|
|
btn.title = '도움말 (F1)';
|
|
btn.setAttribute('aria-label', '도움말');
|
|
btn.onclick = e => { e.stopPropagation(); showHelp(topic); };
|
|
el.style.position = 'relative';
|
|
el.appendChild(btn);
|
|
});
|
|
});
|
|
}
|
|
|
|
// ── 전역 ? 도움말 버튼 ─────────────────────────────────────
|
|
function buildGlobalHelpBtn() {
|
|
if (document.getElementById('grd-global-help')) return;
|
|
const btn = document.createElement('button');
|
|
btn.id = 'grd-global-help';
|
|
btn.textContent = '?';
|
|
btn.title = 'GUARDiA 도움말 (F1)';
|
|
btn.setAttribute('aria-label', '도움말');
|
|
btn.style.cssText = `
|
|
position:fixed; right:70px; bottom:20px; z-index:8999;
|
|
width:44px; height:44px; border-radius:50%;
|
|
background:#4f46e5; color:#fff; border:none;
|
|
font-size:18px; font-weight:700; cursor:pointer;
|
|
box-shadow:0 4px 16px rgba(79,70,229,.4);
|
|
display:flex; align-items:center; justify-content:center;
|
|
transition:transform .2s;
|
|
`;
|
|
btn.onmouseover = () => btn.style.transform = 'scale(1.1)';
|
|
btn.onmouseout = () => btn.style.transform = '';
|
|
btn.onclick = () => showHelp();
|
|
document.body.appendChild(btn);
|
|
}
|
|
|
|
// ── 키보드 단축키 ──────────────────────────────────────────
|
|
document.addEventListener('keydown', e => {
|
|
if (e.key === 'F1') { e.preventDefault(); showHelp(); }
|
|
if (e.key === 'Escape') {
|
|
const ol = document.getElementById('grd-help-overlay');
|
|
if (ol && ol.style.display !== 'none') closeHelp();
|
|
}
|
|
});
|
|
|
|
// ── 초기화 ─────────────────────────────────────────────────
|
|
function init() {
|
|
buildGlobalHelpBtn();
|
|
injectHelpButtons();
|
|
// SPA 뷰 변화 시 버튼 재삽입
|
|
new MutationObserver(() => injectHelpButtons())
|
|
.observe(document.body, { childList: true, subtree: true });
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
setTimeout(init, 300);
|
|
}
|
|
|
|
// 전역 노출
|
|
window.GUARDiAHelp = { show: showHelp, close: closeHelp };
|
|
|
|
})();
|