zioinfo-mail/workspace/guardia-itsm/static/index.html
DESKTOP-TKLFCPR\ython cfe2901a55 refactor(structure): consolidate all projects under workspace/
- 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>
2026-05-31 23:50:56 +09:00

795 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GUARDiA ITSM — AI 기반 레거시 인프라 자율 운영 플랫폼</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
<!-- Chart.js — 대시보드 차트 (CDN 실패 시 로컬 폴백) -->
<script>
(function() {
var s = document.createElement('script');
s.src = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js';
s.crossOrigin = 'anonymous';
s.onerror = function() {
// 폐쇄망 폴백: /static/chart.umd.min.js (오프라인 설치 시 복사됨)
var local = document.createElement('script');
local.src = '/static/chart.umd.min.js';
local.onerror = function() { window._chartjsUnavailable = true; };
document.head.appendChild(local);
};
document.head.appendChild(s);
})();
</script>
<!-- F-4: PWA -->
<link rel="manifest" href="/static/manifest.json">
<meta name="theme-color" content="#4f8ef7">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="GUARDiA">
<link rel="apple-touch-icon" href="/static/icons/icon-192.png">
</head>
<body>
<!-- FOUC 방지: body 첫 번째 자식으로 테마를 즉시 적용 -->
<script>document.body.dataset.theme = localStorage.getItem("guardia_theme") || "dark";</script>
<div id="app">
<!-- ── Sidebar ───────────────────────────────────── -->
<aside id="sidebar">
<div id="sidebar-logo">
<img src="/static/icons/logo.png" alt="GUARDiA 로고"
style="height:36px;width:auto;object-fit:contain;margin-right:10px"
onerror="this.style.display='none';document.getElementById('logo-fallback').style.display='flex'">
<div id="logo-fallback" style="display:none;align-items:center;gap:8px">
<div class="logo-icon">G</div>
</div>
<div>
<div class="logo-title">GUARDiA ITSM</div>
<div class="logo-sub">인프라 자동화 플랫폼</div>
</div>
</div>
<nav id="sidebar-nav">
<!-- 대시보드 -->
<div class="nav-item active" data-view="dashboard">
<span class="nav-icon">📊</span> 대시보드
</div>
<!-- SR 관리 -->
<div class="nav-group-header" onclick="toggleNavGroup(this)" aria-expanded="false">
<span class="nav-icon">🗂️</span><span>SR 관리</span>
<span class="nav-arrow" aria-hidden="true"></span>
</div>
<div class="nav-group-body" role="group">
<div class="nav-sub-item" data-view="board">칸반 보드</div>
<div class="nav-sub-item" data-view="list">SR 목록</div>
<div class="nav-sub-item" data-view="audit">감사 로그</div>
</div>
<!-- PMS 프로젝트 관리 -->
<div class="nav-group-header" onclick="toggleNavGroup(this)" aria-expanded="false">
<span class="nav-icon">🏗️</span><span>PMS 프로젝트</span>
<span class="nav-arrow" aria-hidden="true"></span>
</div>
<div class="nav-group-body" role="group">
<a class="nav-sub-item" href="/si">프로젝트 목록</a>
<a class="nav-sub-item" href="/si?tab=wbs">WBS 관리</a>
<a class="nav-sub-item" href="/si?tab=deliverables">산출물 관리</a>
<a class="nav-sub-item" href="/si?tab=issues">이슈 관리</a>
<a class="nav-sub-item" href="/si?tab=risks">위험 관리</a>
<a class="nav-sub-item" href="/si?tab=report">보고서 (일/주/월)</a>
</div>
<!-- 인프라 -->
<div class="nav-group-header" onclick="toggleNavGroup(this)" aria-expanded="false">
<span class="nav-icon">🖥️</span><span>인프라</span>
<span class="nav-arrow" aria-hidden="true"></span>
</div>
<div class="nav-group-body" role="group">
<div class="nav-sub-item" data-view="cmdb">CMDB</div>
<div class="nav-sub-item" data-view="kb">기술 문서 KB</div>
<div class="nav-sub-item" data-view="institutions">기관/사이트</div>
</div>
<div class="nav-separator"></div>
<a class="nav-item nav-link-ext" href="/incidents">
<span class="nav-icon">🚨</span> 장애 관리
</a>
<a class="nav-item nav-link-ext" href="/ssl">
<span class="nav-icon">🔑</span> SSL 관리
</a>
<a class="nav-item nav-link-ext" href="/pm">
<span class="nav-icon">🔧</span> PM 점검
</a>
<a class="nav-item nav-link-ext" href="/oncall">
<span class="nav-icon">📞</span> 온콜 관리
</a>
<a class="nav-item nav-link-ext" href="/batch">
<span class="nav-icon">⚙️</span> 배치 작업
</a>
<a class="nav-item nav-link-ext" href="/vibe">
<span class="nav-icon">🎸</span> 바이브 코딩
</a>
<a class="nav-item nav-link-ext" href="/si">
<span class="nav-icon">🏗️</span> SI 프로젝트
</a>
<a class="nav-item nav-link-ext" href="/agents">
<span class="nav-icon">🤖</span> AI 에이전트
</a>
<div class="nav-separator"></div>
<div class="nav-item" data-view="institutions">
<span class="nav-icon">🏢</span> 기관 관리
</div>
<div class="nav-item" data-view="scripts">
<span class="nav-icon">📜</span> 스크립트 관리
</div>
<div class="nav-item" data-view="timetable">
<span class="nav-icon">📅</span> 작업 타임테이블
</div>
<div class="nav-separator"></div>
<a class="nav-item nav-link-ext" href="/license" id="nav-license">
<span class="nav-icon">🔏</span> 라이선스 관리
</a>
</nav>
<div id="sidebar-footer">
<div class="status-dot online"></div>
<span>시스템 정상</span>
</div>
<div style="padding:8px 16px;font-size:10px;color:var(--text-muted);line-height:1.5;border-top:1px solid rgba(255,255,255,.06)">
<img src="/static/icons/logo.png" alt="" style="height:18px;vertical-align:middle;margin-right:4px;opacity:.6"
onerror="this.style.display='none'">
Copyright &copy; 2026 GUARDiA<br>
All Rights Reserved.
</div>
<!-- 사용자 정보 + 로그아웃 -->
<div id="sidebar-user">
<div id="sidebar-user-info">
<div id="user-display-name" style="font-size:13px;color:var(--text-bright);font-weight:600"></div>
<div id="user-role-badge"></div>
</div>
<button class="btn-logout" onclick="logout()" title="로그아웃"></button>
</div>
<!-- 테마 전환 -->
<div id="theme-switcher">
<div class="theme-switcher-label">테마</div>
<div class="theme-swatches">
<button class="theme-swatch" data-theme="dark" onclick="applyTheme('dark')">
<span class="theme-swatch-icon">🌑</span>
<span class="theme-swatch-name">다크</span>
</button>
<button class="theme-swatch" data-theme="light" onclick="applyTheme('light')">
<span class="theme-swatch-icon">☀️</span>
<span class="theme-swatch-name">라이트</span>
</button>
<button class="theme-swatch" data-theme="midnight" onclick="applyTheme('midnight')">
<span class="theme-swatch-icon">🌌</span>
<span class="theme-swatch-name">미드나잇</span>
</button>
</div>
</div>
<!-- SSE 연결 상태 표시 -->
<div id="sse-status" title="실시간 연결 상태">
<span id="sse-dot" class="sse-dot grey"></span>
<span id="sse-label" style="font-size:10px;color:var(--text-muted)">연결 중…</span>
</div>
</aside>
<!-- ── Main ──────────────────────────────────────── -->
<main id="main">
<!-- Top bar -->
<header id="topbar">
<h1 id="page-title">대시보드</h1>
<div id="topbar-actions">
<div id="topbar-refresh-indicator" class="refresh-indicator hidden">
<span class="refresh-dot"></span><span style="font-size:11px">데이터 갱신됨</span>
</div>
<button class="btn btn-primary" id="btn-new-sr">+ 새 SR</button>
</div>
</header>
<!-- Views -->
<div id="view-dashboard" class="view active">
<!-- KPI 카드 행 -->
<div class="stats-row" id="stats-row"><!-- filled by JS --></div>
<!-- ── 대시보드 탭 ── -->
<div class="dash-tab-nav" id="dash-tab-nav">
<button class="dash-tab active" data-tab="ops" onclick="switchDashTab('ops')">📊 운영 현황</button>
<button class="dash-tab" data-tab="infra" onclick="switchDashTab('infra')">🖥️ 인프라</button>
<button class="dash-tab" data-tab="security" onclick="switchDashTab('security')">🔒 보안</button>
<button class="dash-tab" data-tab="ai" onclick="switchDashTab('ai')">🤖 AI 인사이트</button>
</div>
<!-- ═══ 탭 1: 운영 현황 ═══ -->
<div class="dash-tab-panel active" id="dash-panel-ops">
<div class="chart-grid-2">
<!-- SR 상태 도넛 -->
<div class="card chart-card">
<div class="card-header">SR 상태 분포</div>
<div class="card-body chart-body">
<canvas id="chart-sr-status"></canvas>
</div>
</div>
<!-- 우선순위 바 -->
<div class="card chart-card">
<div class="card-header">우선순위별 SR</div>
<div class="card-body chart-body">
<canvas id="chart-sr-priority"></canvas>
</div>
</div>
</div>
<!-- 7일 SR 추이 -->
<div class="card" style="margin-top:14px">
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
<span>📈 최근 7일 SR 추이</span>
<button class="btn btn-secondary" style="font-size:11px;padding:3px 8px"
onclick="loadDashboardCharts()">새로고침</button>
</div>
<div class="card-body" style="padding:14px 16px">
<canvas id="chart-sr-trend" style="max-height:200px"></canvas>
</div>
</div>
<!-- 엔지니어 워크로드 + 최근 SR -->
<div class="chart-grid-2" style="margin-top:14px">
<div class="card chart-card">
<div class="card-header">👷 엔지니어 워크로드</div>
<div class="card-body" id="workload-body">
<div style="color:var(--text-muted);font-size:13px;padding:8px">로딩 중…</div>
</div>
</div>
<div class="card chart-card">
<div class="card-header">최근 SR</div>
<div class="card-body" id="recent-list" style="overflow-y:auto;max-height:260px"></div>
</div>
</div>
</div>
<!-- ═══ 탭 2: 인프라 ═══ -->
<div class="dash-tab-panel" id="dash-panel-infra">
<div class="chart-grid-2">
<!-- 기관별 SR 수평 바 -->
<div class="card chart-card">
<div class="card-header">기관별 SR 현황 (Top 10)</div>
<div class="card-body chart-body">
<canvas id="chart-inst-sr"></canvas>
</div>
</div>
<!-- 서버 OS 분포 -->
<div class="card chart-card">
<div class="card-header">서버 OS 분포</div>
<div class="card-body chart-body">
<canvas id="chart-os-dist"></canvas>
</div>
</div>
</div>
<!-- CMDB 서버 상태 그리드 -->
<div class="card" style="margin-top:14px">
<div class="card-header">서버 헬스 현황</div>
<div class="card-body" id="server-health-grid"
style="display:flex;flex-wrap:wrap;gap:8px;padding:14px 16px">
<div style="color:var(--text-muted);font-size:13px">로딩 중…</div>
</div>
</div>
<!-- SSL 만료 현황 -->
<div class="card" style="margin-top:14px">
<div class="card-header">🔐 SSL 인증서 만료 현황</div>
<div class="card-body chart-body" style="max-height:220px">
<canvas id="chart-ssl-expiry"></canvas>
</div>
</div>
</div>
<!-- ═══ 탭 3: 보안 ═══ -->
<div class="dash-tab-panel" id="dash-panel-security">
<div class="chart-grid-2">
<!-- 취약점 심각도 바 -->
<div class="card chart-card">
<div class="card-header">취약점 심각도 분포</div>
<div class="card-body chart-body">
<canvas id="chart-vuln-severity"></canvas>
</div>
</div>
<!-- 패치율 도넛 -->
<div class="card chart-card">
<div class="card-header">패치 적용 현황</div>
<div class="card-body chart-body">
<canvas id="chart-patch-rate"></canvas>
</div>
</div>
</div>
<!-- PAM 세션 + 감사 로그 -->
<div class="chart-grid-2" style="margin-top:14px">
<div class="card chart-card">
<div class="card-header">🔑 PAM 세션 현황</div>
<div class="card-body" id="pam-status-body"
style="padding:14px 16px;font-size:13px">로딩 중…</div>
</div>
<div class="card chart-card">
<div class="card-header">📋 최근 감사 로그</div>
<div class="card-body" id="audit-body"
style="overflow-y:auto;max-height:260px"></div>
</div>
</div>
</div>
<!-- ═══ 탭 4: AI 인사이트 ═══ -->
<div class="dash-tab-panel" id="dash-panel-ai">
<div class="chart-grid-2">
<!-- AI 티켓 분류 분포 -->
<div class="card chart-card">
<div class="card-header">🤖 AI 티켓 분류 현황</div>
<div class="card-body chart-body">
<canvas id="chart-ai-category"></canvas>
</div>
</div>
<!-- 이상 탐지 추이 -->
<div class="card chart-card">
<div class="card-header">⚡ 이상 탐지 추이 (7일)</div>
<div class="card-body chart-body">
<canvas id="chart-anomaly-trend"></canvas>
</div>
</div>
</div>
<!-- 예측 유지보수 + 학습 루프 -->
<div class="chart-grid-2" style="margin-top:14px">
<div class="card chart-card">
<div class="card-header">🔧 예측 유지보수 위험 서버</div>
<div class="card-body" id="predictive-body"
style="padding:14px 16px;font-size:13px">로딩 중…</div>
</div>
<div class="card chart-card">
<div class="card-header">📚 학습 루프 현황</div>
<div class="card-body" id="learning-body"
style="padding:14px 16px;font-size:13px">로딩 중…</div>
</div>
</div>
</div>
</div><!-- /view-dashboard -->
<div id="view-board" class="view">
<div id="kanban-board">
<!-- columns rendered by JS -->
</div>
</div>
<div id="view-list" class="view">
<div class="list-toolbar">
<input type="text" id="search-input" placeholder="SR 제목 검색…" class="search-box">
<select id="filter-status" class="filter-select">
<option value="">전체 상태</option>
<option value="RECEIVED">접수</option>
<option value="PARSED">파싱 완료</option>
<option value="PENDING_APPROVAL">승인 대기</option>
<option value="APPROVED">승인됨</option>
<option value="IN_PROGRESS">진행 중</option>
<option value="PENDING_PM_VALIDATION">PM 검증 대기</option>
<option value="COMPLETED">완료</option>
<option value="FAILED_ROLLBACK">롤백 실패</option>
<option value="REJECTED">반려</option>
</select>
<select id="filter-type" class="filter-select">
<option value="">전체 유형</option>
<option value="DEPLOY">배포</option>
<option value="RESTART">재기동</option>
<option value="LOG">로그</option>
<option value="INQUIRY">문의</option>
<option value="OTHER">기타</option>
</select>
</div>
<table class="sr-table" id="sr-table">
<thead>
<tr>
<th>SR ID</th><th>유형</th><th>제목</th>
<th>상태</th><th>우선순위</th><th>담당자</th><th>요청자</th><th>생성일</th>
</tr>
</thead>
<tbody id="sr-tbody"></tbody>
</table>
</div>
<div id="view-audit" class="view">
<div class="audit-header-row">
<span class="audit-title">감사 로그 (SHA-256 해시 체인)</span>
<button class="btn btn-secondary" id="btn-verify">체인 무결성 검증</button>
<span id="verify-result"></span>
</div>
<table class="sr-table" id="audit-table">
<thead>
<tr>
<th>#</th><th>SR</th><th>행위자</th><th>액션</th><th>내용</th>
<th>해시 (앞 12자)</th><th>시각</th>
</tr>
</thead>
<tbody id="audit-tbody"></tbody>
</table>
</div>
<div id="view-cmdb" class="view">
<div class="cmdb-grid" id="cmdb-grid"><!-- filled by JS --></div>
</div>
<!-- KB 뷰 -->
<div id="view-kb" class="view">
<div class="list-toolbar" style="gap:10px">
<input type="text" id="kb-search-input" placeholder="증상 또는 키워드 검색… (예: OOM, SSL, connection pool)"
class="search-box" style="flex:1">
<select id="kb-category-filter" class="filter-select">
<option value="">전체 카테고리</option>
<option value="JAVA">JAVA</option>
<option value="MIDDLEWARE">MIDDLEWARE</option>
<option value="DB">DB</option>
<option value="WEB">WEB</option>
<option value="OS">OS</option>
<option value="SECURITY">SECURITY</option>
</select>
<button class="btn btn-primary" onclick="searchKB()">검색</button>
</div>
<div id="kb-results"><!-- filled by JS --></div>
</div>
<!-- 기관 관리 뷰 -->
<div id="view-institutions" class="view">
<div class="list-toolbar">
<input type="text" id="inst-search" placeholder="기관명 검색…" class="search-box" oninput="filterInstitutions()">
<select id="inst-region-filter" class="filter-select" onchange="filterInstitutions()">
<option value="">전체 지역</option>
<option value="서울">서울</option>
<option value="세종">세종</option>
<option value="부산">부산</option>
<option value="대전">대전</option>
<option value="기타">기타</option>
</select>
<button class="btn btn-primary" id="btn-new-inst" onclick="openInstModal()">+ 기관 등록</button>
</div>
<table class="sr-table" id="inst-table">
<thead>
<tr>
<th>기관코드</th><th>기관명</th><th>지역</th><th>계약 만료</th>
<th>SLA</th><th>서버</th><th>담당자</th><th>상태</th><th>관리</th>
</tr>
</thead>
<tbody id="inst-tbody"></tbody>
</table>
</div>
<!-- 스크립트 관리 뷰 -->
<div id="view-scripts" class="view">
<div class="list-toolbar">
<input type="text" id="script-search" placeholder="스크립트명·설명·태그 검색…" class="search-box" oninput="filterScripts()">
<select id="script-category-filter" class="filter-select" onchange="filterScripts()">
<option value="">전체 카테고리</option>
<option value="SM">SM (일상운영)</option>
<option value="REGULAR">정기점검</option>
<option value="ADHOC">수시점검</option>
<option value="DEPLOY">배포</option>
<option value="SECURITY">보안</option>
<option value="MONITORING">모니터링</option>
</select>
<select id="script-layer-filter" class="filter-select" onchange="filterScripts()">
<option value="">전체 레이어</option>
<option value="WEB">WEB</option>
<option value="WAS">WAS</option>
<option value="DB">DB</option>
<option value="ALL">공통</option>
</select>
<button class="btn btn-primary" onclick="openScriptModal()">+ 스크립트 등록</button>
</div>
<div id="script-list-body"></div>
</div>
<!-- 작업 타임테이블 뷰 -->
<div id="view-timetable" class="view">
<div class="list-toolbar">
<input type="text" id="tt-search" placeholder="제목·내용 검색…" class="search-box" oninput="filterTimetable()">
<select id="tt-type-filter" class="filter-select" onchange="filterTimetable()">
<option value="">전체 유형</option>
<option value="REGULAR_CHECK">정기점검</option>
<option value="PM">예방정비</option>
<option value="SR">SR작업</option>
<option value="ADHOC">수시점검</option>
<option value="DEPLOY">배포</option>
<option value="EMERGENCY">긴급대응</option>
</select>
<select id="tt-status-filter" class="filter-select" onchange="filterTimetable()">
<option value="">전체 결과</option>
<option value="PENDING">예정</option>
<option value="SUCCESS">완료</option>
<option value="FAILED">실패</option>
<option value="PARTIAL">부분완료</option>
<option value="CANCELLED">취소</option>
</select>
<button class="btn btn-primary" onclick="openTimetableModal()">+ 작업 등록</button>
<button class="btn btn-secondary" onclick="exportTimetableExcel()" title="Excel 다운로드">📥 Excel</button>
</div>
<table class="sr-table" id="tt-table">
<thead>
<tr>
<th>유형</th><th>제목</th><th>기관</th><th>처리예정</th>
<th>완료</th><th>결과상태</th><th>담당자</th><th>SR</th><th>관리</th>
</tr>
</thead>
<tbody id="tt-tbody"></tbody>
</table>
</div>
</main>
</div>
<!-- ── SR Detail Modal ────────────────────────────── -->
<div id="modal-overlay" class="hidden">
<div id="modal">
<button class="modal-close" id="modal-close-btn">×</button>
<div id="modal-body"><!-- filled by JS --></div>
</div>
</div>
<!-- ── New SR Modal ───────────────────────────────── -->
<div id="new-sr-overlay" class="hidden">
<div id="new-sr-modal">
<button class="modal-close" id="new-sr-close">×</button>
<h2>새 SR 생성</h2>
<form id="new-sr-form">
<label>제목 <input type="text" name="title" required></label>
<label>설명 <textarea name="description" rows="3"></textarea></label>
<div class="form-row">
<label>유형
<select name="sr_type">
<option value="OTHER">기타</option>
<option value="DEPLOY">배포</option>
<option value="RESTART">재기동</option>
<option value="LOG">로그</option>
<option value="INQUIRY">문의</option>
</select>
</label>
<label>우선순위
<select name="priority">
<option value="MEDIUM">보통</option>
<option value="CRITICAL">긴급</option>
<option value="HIGH">높음</option>
<option value="LOW">낮음</option>
</select>
</label>
</div>
<div class="form-row">
<label>요청자 <input type="text" name="requested_by" required></label>
<label>기관코드 <input type="text" name="inst_code" placeholder="MOF / MOIS / MSS"></label>
</div>
<label>대상 서버 <input type="text" name="target_server" placeholder="예: MOF-WAS-01"></label>
<div class="file-upload-area">
<label class="file-upload-label">
<span class="file-upload-icon">📎</span>
<span id="file-upload-text">첨부파일 선택 (선택사항, 최대 10개 · 파일당 20 MB)</span>
<input type="file" id="sr-file-input" multiple accept=".pdf,.txt,.log,.csv,.png,.jpg,.jpeg,.xlsx,.docx,.zip,.sh,.sql,.json,.yaml,.md" style="display:none">
</label>
<div id="file-preview-list"></div>
</div>
<button type="submit" class="btn btn-primary" style="width:100%;margin-top:8px">생성</button>
</form>
</div>
</div>
<!-- ── AI 명령 채팅 패널 ──────────────────────────── -->
<div id="ai-chat-panel" class="hidden">
<div id="ai-chat-header">
<span>🤖 GUARDiA AI 어시스턴트</span>
<button id="ai-chat-close" title="닫기">×</button>
</div>
<div id="ai-chat-messages"></div>
<div id="ai-chat-suggestions"></div>
<div id="ai-chat-input-row">
<input type="text" id="ai-chat-input"
placeholder="자연어로 명령하세요… 예: 승인 대기 SR 목록 보여줘">
<button id="ai-chat-send">전송</button>
</div>
</div>
<button id="ai-chat-fab" title="AI 명령 어시스턴트">🤖</button>
<!-- ── 기관 등록/수정 모달 ────────────────────────── -->
<div id="inst-modal-overlay" class="hidden">
<div id="inst-modal" class="modal-wide">
<button class="modal-close" onclick="closeInstModal()">×</button>
<h2 id="inst-modal-title">기관 등록</h2>
<form id="inst-form" onsubmit="submitInstForm(event)">
<div class="form-row">
<label>기관코드 <input type="text" name="inst_code" placeholder="MOF" required></label>
<label>기관명 <input type="text" name="inst_name" required></label>
</div>
<div class="form-row">
<label>기관유형 <input type="text" name="org_type" placeholder="중앙행정기관"></label>
<label>지역
<select name="region">
<option value="">선택</option>
<option value="서울">서울</option>
<option value="세종">세종</option>
<option value="부산">부산</option>
<option value="대전">대전</option>
<option value="기타">기타</option>
</select>
</label>
</div>
<label>주소 <input type="text" name="address"></label>
<div class="form-row">
<label>대표 전화 <input type="text" name="phone" placeholder="044-000-0000"></label>
<label>SLA (시간) <input type="number" name="sla_hours" value="4" min="1" max="72"></label>
</div>
<div class="form-row">
<label>계약 시작 <input type="date" name="contract_start"></label>
<label>계약 만료 <input type="date" name="contract_end"></label>
</div>
<label>담당 PM <input type="text" name="contact_pm"></label>
<label>메모 <textarea name="note" rows="2"></textarea></label>
<button type="submit" class="btn btn-primary" style="width:100%;margin-top:8px">저장</button>
</form>
</div>
</div>
<!-- ── 담당자 등록/수정 모달 ──────────────────────── -->
<div id="contact-modal-overlay" class="hidden">
<div id="contact-modal">
<button class="modal-close" onclick="closeContactModal()">×</button>
<h2 id="contact-modal-title">담당자 등록</h2>
<form id="contact-form" onsubmit="submitContactForm(event)">
<div class="form-row">
<label>이름 <input type="text" name="contact_name" required></label>
<label>역할
<select name="role">
<option value="MANAGER">담당자(관리)</option>
<option value="ENGINEER">엔지니어</option>
<option value="PM">PM</option>
<option value="SECURITY">보안담당</option>
<option value="HELPDESK">헬프데스크</option>
</select>
</label>
</div>
<div class="form-row">
<label>부서 <input type="text" name="dept"></label>
<label>직책 <input type="text" name="position"></label>
</div>
<div class="form-row">
<label>이메일 <input type="email" name="email" placeholder="user@agency.go.kr"></label>
<label>직통 전화 <input type="text" name="phone"></label>
</div>
<div class="form-row">
<label>휴대폰 <input type="text" name="mobile" placeholder="010-0000-0000"></label>
<label style="align-items:center;gap:8px">
주 담당자
<input type="checkbox" name="is_primary" style="width:auto;margin-top:4px">
</label>
</div>
<label>메모 <textarea name="note" rows="2"></textarea></label>
<button type="submit" class="btn btn-primary" style="width:100%;margin-top:8px">저장</button>
</form>
</div>
</div>
<!-- ── 스크립트 등록/수정 모달 ───────────────────── -->
<div id="script-modal-overlay" class="hidden">
<div id="script-modal" class="modal-wide modal-xlarge">
<button class="modal-close" onclick="closeScriptModal()">×</button>
<h2 id="script-modal-title">스크립트 등록</h2>
<form id="script-form" onsubmit="submitScriptForm(event)">
<label>스크립트명 <input type="text" name="script_name" required></label>
<div class="form-row">
<label>카테고리
<select name="category">
<option value="SM">SM (일상운영)</option>
<option value="REGULAR">정기점검</option>
<option value="ADHOC">수시점검</option>
<option value="DEPLOY">배포</option>
<option value="SECURITY">보안</option>
<option value="MONITORING">모니터링</option>
</select>
</label>
<label>세부분류 <input type="text" name="sub_category" placeholder="DISK_CHECK"></label>
</div>
<div class="form-row">
<label>대상 레이어
<select name="target_layer">
<option value="ALL">전체 (ALL)</option>
<option value="WEB">WEB</option>
<option value="WAS">WAS</option>
<option value="DB">DB</option>
<option value="ESB">ESB</option>
</select>
</label>
<label>OS 유형
<select name="os_type">
<option value="LINUX">LINUX</option>
<option value="WINDOWS">WINDOWS</option>
<option value="ALL">ALL</option>
</select>
</label>
</div>
<label>설명 <textarea name="description" rows="2" required></textarea></label>
<label>스크립트 내용
<textarea name="script_body" rows="10" class="code-textarea" placeholder="#!/bin/bash&#10;# 스크립트 내용 입력" required></textarea>
</label>
<label>태그 (쉼표 구분) <input type="text" name="tags" placeholder="health,check,linux"></label>
<div class="form-row" style="align-items:center">
<label style="flex-direction:row;align-items:center;gap:8px">
<input type="checkbox" name="is_dangerous" style="width:auto"> 위험 명령 포함
</label>
<label style="flex-direction:row;align-items:center;gap:8px">
<input type="checkbox" name="requires_approval" style="width:auto"> 실행 전 승인 필요
</label>
</div>
<button type="submit" class="btn btn-primary" style="width:100%;margin-top:8px">저장</button>
</form>
</div>
</div>
<!-- ── 타임테이블 등록/수정 모달 ─────────────────── -->
<div id="tt-modal-overlay" class="hidden">
<div id="tt-modal" class="modal-wide">
<button class="modal-close" onclick="closeTimetableModal()">×</button>
<h2 id="tt-modal-title">작업 등록</h2>
<form id="tt-form" onsubmit="submitTimetableForm(event)">
<div class="form-row">
<label>작업유형
<select name="work_type" required>
<option value="REGULAR_CHECK">정기점검</option>
<option value="PM">예방정비</option>
<option value="SR">SR작업</option>
<option value="ADHOC">수시점검</option>
<option value="DEPLOY">배포</option>
<option value="EMERGENCY">긴급대응</option>
</select>
</label>
<label>기관
<select name="inst_id" id="tt-inst-select">
<option value="">선택 안 함</option>
</select>
</label>
</div>
<label>제목 <input type="text" name="title" required></label>
<div class="form-row">
<label>처리예정 <input type="datetime-local" name="scheduled_at" required></label>
<label>SR번호 <input type="text" name="sr_id" placeholder="SR-YYYYMMDD-XXXXXX"></label>
</div>
<div class="form-row">
<label>시작일시 <input type="datetime-local" name="started_at"></label>
<label>완료일시 <input type="datetime-local" name="completed_at"></label>
</div>
<label>처리내용 <textarea name="content" rows="3" required></textarea></label>
<label>명령어/쉘 스크립트
<select name="script_id" id="tt-script-select" onchange="fillScriptBody(this)">
<option value="">직접 입력</option>
</select>
</label>
<label>명령어 직접 입력 <textarea name="command_or_shell" rows="3" class="code-textarea"></textarea></label>
<label>처리결과 <textarea name="result" rows="3"></textarea></label>
<div class="form-row">
<label>결과상태
<select name="result_status">
<option value="PENDING">예정</option>
<option value="SUCCESS">완료</option>
<option value="FAILED">실패</option>
<option value="PARTIAL">부분완료</option>
<option value="CANCELLED">취소</option>
</select>
</label>
<label>담당자 <input type="text" name="assignee" placeholder="사번 또는 이름"></label>
</div>
<div class="form-row">
<label>검토자 <input type="text" name="reviewer" placeholder="PM 사번 또는 이름"></label>
<label>비고 <input type="text" name="note"></label>
</div>
<button type="submit" class="btn btn-primary" style="width:100%;margin-top:8px">저장</button>
</form>
</div>
</div>
<script src="/static/app.js"></script>
<!-- 온보딩 가이드 챗봇 — 설치 완료 후 자동 실행 -->
<script src="/static/onboarding.js"></script>
<!-- 화면별 도움말 시스템 (GS인증 사용성 요구사항 F1/? 버튼) -->
<script src="/static/help.js"></script>
</body>
</html>