972819b492
- ApexCharts 기반 실시간 모니터링 대시보드 - 어댑터/배치/서버/소켓/트랜잭션 모니터링 컴포넌트 - 처리량 계산 및 데이터 매핑 서비스
957 lines
33 KiB
Plaintext
957 lines
33 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
|
<%@ page import="java.util.*" %>
|
|
|
|
<!-- 소켓 상세 정보 모달 -->
|
|
<div id="socketDetailModal" class="modal modal-modern">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title">회선 상세 정보</h2>
|
|
<span class="close-modal">×</span>
|
|
</div>
|
|
<div class="modal-body">
|
|
<!-- 탭 메뉴 -->
|
|
<div class="socket-detail-tabs">
|
|
<button class="tab-btn active" data-tab="institution-visual">
|
|
<i class="bi bi-pie-chart"></i> 기관 현황
|
|
</button>
|
|
<button class="tab-btn" data-tab="institution-status">
|
|
<i class="bi bi-building"></i> 기관 상태
|
|
</button>
|
|
<button class="tab-btn" data-tab="socket-status">
|
|
<i class="bi bi-plug"></i> 어댑터 상태
|
|
</button>
|
|
<button class="tab-btn" data-tab="socket-process">
|
|
<i class="bi bi-arrow-left-right"></i> 세션 상태
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 탭 컨텐츠 -->
|
|
<div class="socket-detail-content">
|
|
<!-- 기관 연결 현황 시각화 탭 -->
|
|
<div id="institution-visual" class="tab-content active">
|
|
<div class="visual-stats-summary">
|
|
<div class="stats-card total-institutions">
|
|
<div class="stats-icon"><i class="bi bi-building"></i></div>
|
|
<div class="stats-details">
|
|
<div class="stats-number" id="total-institutions-count">0</div>
|
|
<div class="stats-label">총 기관</div>
|
|
</div>
|
|
</div>
|
|
<div class="stats-card normal-institutions">
|
|
<div class="stats-icon"><i class="bi bi-check-circle"></i></div>
|
|
<div class="stats-details">
|
|
<div class="stats-number" id="normal-institutions-count">0</div>
|
|
<div class="stats-label">정상</div>
|
|
</div>
|
|
</div>
|
|
<div class="stats-card warning-institutions">
|
|
<div class="stats-icon"><i class="bi bi-exclamation-triangle"></i></div>
|
|
<div class="stats-details">
|
|
<div class="stats-number" id="warning-institutions-count">0</div>
|
|
<div class="stats-label">경고</div>
|
|
</div>
|
|
</div>
|
|
<div class="stats-card error-institutions">
|
|
<div class="stats-icon"><i class="bi bi-x-circle"></i></div>
|
|
<div class="stats-details">
|
|
<div class="stats-number" id="error-institutions-count">0</div>
|
|
<div class="stats-label">오류</div>
|
|
</div>
|
|
</div>
|
|
<div class="stats-card inactive-institutions">
|
|
<div class="stats-icon"><i class="bi bi-dash-circle"></i></div>
|
|
<div class="stats-details">
|
|
<div class="stats-number" id="inactive-institutions-count">0</div>
|
|
<div class="stats-label">미사용</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="connection-visual-container">
|
|
<div id="institution-grid" class="institution-grid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 기관 상태 탭 -->
|
|
<div id="institution-status" class="tab-content">
|
|
<div class="institution-status-container">
|
|
<table class="institution-status-table modal-table">
|
|
<thead>
|
|
<tr>
|
|
<th>기관명</th>
|
|
<th>담당자명</th>
|
|
<th>연락처</th>
|
|
<th>정상어댑터</th>
|
|
<th>오류어댑터</th>
|
|
<th>회선상태</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- 데이터가 동적으로 추가됩니다 -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 회선 상태 탭 -->
|
|
<div id="socket-status" class="tab-content">
|
|
<div class="socket-status-container">
|
|
<table class="socket-status-table modal-table">
|
|
<thead>
|
|
<tr>
|
|
<th>어댑터 그룹</th>
|
|
<th>인스턴스명</th>
|
|
<th>어댑터명</th>
|
|
<th>어댑터 설명</th>
|
|
<th>어댑터 유형</th>
|
|
<th>상태</th>
|
|
<th>기동 상태</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- 데이터가 동적으로 추가됩니다 -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 회선 연결 상태 탭 -->
|
|
<div id="socket-process" class="tab-content">
|
|
<div class="socket-process-container">
|
|
<table class="socket-process-table modal-table">
|
|
<thead>
|
|
<tr>
|
|
<th>어댑터 그룹</th>
|
|
<th>인스턴스명</th>
|
|
<th>어댑터명</th>
|
|
<th>어댑터 설명</th>
|
|
<th>최대 연결</th>
|
|
<th>현재 연결</th>
|
|
<th>송신 수</th>
|
|
<th>수신 수</th>
|
|
<th>오류 수</th>
|
|
<th>상태</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- 데이터가 동적으로 추가됩니다 -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-primary refresh-socket-data">
|
|
<i class="bi bi-arrow-clockwise" style="color: #ffffff;"></i> 새로고침
|
|
</button>
|
|
<button class="btn btn-secondary close-modal-btn">닫기</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
/* 기존 스타일 강화 */
|
|
.modal-modern {
|
|
display: none;
|
|
position: fixed;
|
|
z-index: 1000;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: auto;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
backdrop-filter: blur(4px);
|
|
}
|
|
|
|
.modal-modern .modal-content {
|
|
position: relative;
|
|
background-color: #fff;
|
|
margin: 2% auto;
|
|
padding: 0;
|
|
width: 95%;
|
|
max-width: 1400px;
|
|
border: none;
|
|
border-radius: 16px;
|
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
|
|
animation: modalSlideUp 0.3s;
|
|
}
|
|
|
|
@keyframes modalSlideUp {
|
|
from {opacity: 0; transform: translateY(30px);}
|
|
to {opacity: 1; transform: translateY(0);}
|
|
}
|
|
|
|
@keyframes modalFadeIn {
|
|
from {opacity: 0;}
|
|
to {opacity: 1;}
|
|
}
|
|
|
|
.modal-modern .modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 1.5rem 2rem;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
background-color: #f8f9fa;
|
|
border-radius: 16px 16px 0 0;
|
|
}
|
|
|
|
.modal-modern .modal-title {
|
|
margin: 0;
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.modal-modern .close-modal {
|
|
color: #aaa;
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
transition: color 0.2s;
|
|
width: 36px;
|
|
height: 36px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.modal-modern .close-modal:hover {
|
|
color: #333;
|
|
background-color: rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.modal-modern .modal-body {
|
|
padding: 1.5rem 2rem;
|
|
max-height: 75vh;
|
|
overflow-y: auto;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.modal-modern .modal-footer {
|
|
padding: 1.2rem 2rem;
|
|
border-top: 1px solid #e0e0e0;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 1rem;
|
|
border-radius: 0 0 16px 16px;
|
|
background-color: #fff;
|
|
}
|
|
|
|
/* 탭 스타일 향상 */
|
|
.socket-detail-tabs {
|
|
display: flex;
|
|
border-bottom: none;
|
|
margin-bottom: 1.5rem;
|
|
overflow-x: auto;
|
|
background-color: #fff;
|
|
border-radius: 8px;
|
|
padding: 0.5rem;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.tab-btn {
|
|
padding: 0.9rem 1.5rem;
|
|
background: none;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
font-size: 1rem;
|
|
color: #666;
|
|
transition: all 0.2s;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.tab-btn:hover {
|
|
color: #333;
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.tab-btn.active {
|
|
color: white;
|
|
background-color: #007bff;
|
|
box-shadow: 0 4px 8px rgba(0, 123, 255, 0.2);
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
animation: fadeIn 0.3s;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {opacity: 0;}
|
|
to {opacity: 1;}
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
|
|
/* 테이블 스타일 향상 */
|
|
.modal-table {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
background-color: #fff;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.modal-table th {
|
|
background-color: #007bff;
|
|
color: white;
|
|
padding: 1.2rem;
|
|
text-align: left;
|
|
font-weight: 800;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.modal-table td {
|
|
padding: 1rem;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.modal-table tbody tr {
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.modal-table tbody tr:hover {
|
|
background-color: rgba(0, 0, 0, 0.03);
|
|
}
|
|
|
|
.modal-table tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
/* 버튼 스타일 향상 */
|
|
.btn {
|
|
padding: 0.8rem 1.5rem;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
font-size: inherit;
|
|
transition: all 0.25s;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.btn-primary {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background-color: #0069d9;
|
|
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background-color: #f1f5f9;
|
|
color: #333;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background-color: #e2e8f0;
|
|
}
|
|
|
|
/* 로딩 애니메이션 향상 */
|
|
.loading-indicator {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 200px;
|
|
}
|
|
|
|
.loading-spinner {
|
|
border: 4px solid rgba(0, 0, 0, 0.1);
|
|
border-radius: 50%;
|
|
border-top: 4px solid #007bff;
|
|
width: 50px;
|
|
height: 50px;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* 통계 카드 스타일 향상 */
|
|
.visual-stats-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.stats-card {
|
|
background-color: #fff;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
padding: 1.2rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.stats-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
.stats-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.4rem;
|
|
}
|
|
|
|
.stats-details {
|
|
flex: 1;
|
|
}
|
|
|
|
.stats-number {
|
|
font-size: 1.8rem;
|
|
font-weight: 700;
|
|
line-height: 1.1;
|
|
}
|
|
|
|
.stats-label {
|
|
font-size: 0.9rem;
|
|
color: #666;
|
|
margin-top: 0.2rem;
|
|
}
|
|
|
|
/* 카드 색상 */
|
|
.total-institutions .stats-icon {
|
|
background-color: rgba(0, 123, 255, 0.3);
|
|
color: #007bff;
|
|
}
|
|
|
|
.normal-institutions .stats-icon {
|
|
background-color: rgba(40, 167, 69, 0.3);
|
|
color: #28a745;
|
|
}
|
|
|
|
.warning-institutions .stats-icon {
|
|
background-color: rgba(255, 193, 7, 0.3);
|
|
color: #ffc107;
|
|
}
|
|
|
|
.error-institutions .stats-icon {
|
|
background-color: rgba(220, 53, 69, 0.3);
|
|
color: #dc3545;
|
|
}
|
|
|
|
.inactive-institutions .stats-icon {
|
|
background-color: rgba(108, 117, 125, 0.3);
|
|
color: #6c757d;
|
|
}
|
|
|
|
/* 기관 카드 그리드 향상 */
|
|
.connection-visual-container {
|
|
background-color: #fff;
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.institution-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.institution-card {
|
|
background-color: #f8f9fa;
|
|
border-radius: 12px;
|
|
padding: 1.2rem;
|
|
transition: all 0.3s ease;
|
|
border-left: 5px solid #ccc;
|
|
cursor: pointer;
|
|
position: relative;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.institution-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.institution-status-indicator {
|
|
position: absolute;
|
|
top: 15px;
|
|
right: 15px;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.institution-name {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
color: #333;
|
|
padding-right: 2rem;
|
|
}
|
|
|
|
.institution-info {
|
|
display: grid;
|
|
grid-template-columns: 80px 1fr;
|
|
margin-bottom: 0.5rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.institution-info-label {
|
|
color: #666;
|
|
}
|
|
|
|
.institution-stats {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 0.5rem;
|
|
margin-top: 1rem;
|
|
border-top: 1px solid #e0e0e0;
|
|
padding-top: 0.8rem;
|
|
}
|
|
|
|
.institution-stat {
|
|
text-align: center;
|
|
padding: 0.5rem;
|
|
border-radius: 8px;
|
|
background-color: rgba(0, 0, 0, 0.03);
|
|
}
|
|
|
|
/* 상태 배지 스타일 */
|
|
.status-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
padding: 0.4rem 0.8rem;
|
|
border-radius: 50px;
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.status-badge-normal {
|
|
background-color: rgba(40, 167, 69, 0.15);
|
|
color: #28a745;
|
|
}
|
|
|
|
.status-badge-warning {
|
|
background-color: rgba(255, 193, 7, 0.15);
|
|
color: #ffc107;
|
|
}
|
|
|
|
.status-badge-error {
|
|
background-color: rgba(220, 53, 69, 0.15);
|
|
color: #dc3545;
|
|
}
|
|
|
|
.status-badge-inactive {
|
|
background-color: rgba(108, 117, 125, 0.15);
|
|
color: #6c757d;
|
|
}
|
|
|
|
/* 반응형 디자인 */
|
|
@media (max-width: 1024px) {
|
|
.visual-stats-summary {
|
|
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
|
|
}
|
|
|
|
.modal-modern .modal-content {
|
|
width: 98%;
|
|
margin: 1% auto;
|
|
}
|
|
|
|
.modal-modern .modal-body {
|
|
padding: 1rem 1.5rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.socket-detail-tabs {
|
|
padding: 0.3rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.tab-btn {
|
|
padding: 0.8rem 1rem;
|
|
font-size: 0.9rem;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.visual-stats-summary {
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 0.8rem;
|
|
}
|
|
|
|
.stats-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.stats-number {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.institution-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
}
|
|
|
|
.modal-modern .modal-header,
|
|
.modal-modern .modal-footer {
|
|
padding: 1rem 1.5rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.visual-stats-summary {
|
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
}
|
|
|
|
.institution-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.modal-modern .modal-body {
|
|
padding: 1rem;
|
|
}
|
|
}
|
|
|
|
.institution-card.status-normal {
|
|
border-left: 5px solid #98e2a6; /* 파스텔 그린 */
|
|
}
|
|
|
|
.institution-card.status-warning {
|
|
border-left: 5px solid #ffe5a3; /* 파스텔 옐로우 */
|
|
}
|
|
|
|
.institution-card.status-error {
|
|
border-left: 5px solid #ffb3b3; /* 파스텔 레드 */
|
|
}
|
|
|
|
.institution-card.status-inactive {
|
|
border-left: 5px solid #d9d9d9; /* 파스텔 그레이 */
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
// 소켓 상세 정보 모달 관련 스크립트
|
|
(function() {
|
|
// 모달 요소
|
|
const modal = document.getElementById('socketDetailModal');
|
|
const closeButtons = modal.querySelectorAll('.close-modal, .close-modal-btn');
|
|
|
|
// 탭 관련 요소
|
|
const tabButtons = modal.querySelectorAll('.tab-btn');
|
|
const tabContents = modal.querySelectorAll('.tab-content');
|
|
|
|
// 테이블 요소
|
|
const institutionTable = modal.querySelector('.institution-status-table tbody');
|
|
const statusTable = modal.querySelector('.socket-status-table tbody');
|
|
const processTable = modal.querySelector('.socket-process-table tbody');
|
|
|
|
// 시각화 요소
|
|
const institutionGrid = document.getElementById('institution-grid');
|
|
const totalInstitutionsCount = document.getElementById('total-institutions-count');
|
|
const normalInstitutionsCount = document.getElementById('normal-institutions-count');
|
|
const warningInstitutionsCount = document.getElementById('warning-institutions-count');
|
|
const errorInstitutionsCount = document.getElementById('error-institutions-count');
|
|
const inactiveInstitutionsCount = document.getElementById('inactive-institutions-count');
|
|
|
|
// 현재 선택된 서버와 어댑터 그룹
|
|
let currentServerName = '';
|
|
let currentAdapterGroup = '';
|
|
|
|
// 모달 닫기 이벤트
|
|
closeButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
modal.style.display = 'none';
|
|
});
|
|
});
|
|
|
|
// 모달 외부 클릭 시 닫기
|
|
window.addEventListener('click', (event) => {
|
|
if (event.target === modal) {
|
|
modal.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// 탭 전환 이벤트
|
|
tabButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
// 활성 탭 변경
|
|
tabButtons.forEach(btn => btn.classList.remove('active'));
|
|
button.classList.add('active');
|
|
|
|
// 탭 컨텐츠 변경
|
|
const tabId = button.getAttribute('data-tab');
|
|
tabContents.forEach(content => {
|
|
content.classList.remove('active');
|
|
if (content.id === tabId) {
|
|
content.classList.add('active');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// 새로고침 버튼 이벤트
|
|
const refreshButton = modal.querySelector('.refresh-socket-data');
|
|
refreshButton.addEventListener('click', () => {
|
|
loadSocketData(currentServerName, currentAdapterGroup);
|
|
});
|
|
|
|
// 소켓 데이터 로드 함수
|
|
function loadSocketData(serverName, adapterGroup) {
|
|
// 현재 선택된 서버와 어댑터 그룹 저장
|
|
currentServerName = serverName || '';
|
|
currentAdapterGroup = adapterGroup || '';
|
|
|
|
// 로딩 표시
|
|
institutionTable.innerHTML = '<tr><td colspan="6"><div class="loading-indicator"><div class="loading-spinner"></div></div></td></tr>';
|
|
statusTable.innerHTML = '<tr><td colspan="7"><div class="loading-indicator"><div class="loading-spinner"></div></div></td></tr>';
|
|
processTable.innerHTML = '<tr><td colspan="10"><div class="loading-indicator"><div class="loading-spinner"></div></div></td></tr>';
|
|
institutionGrid.innerHTML = '<div class="loading-indicator"><div class="loading-spinner"></div></div>';
|
|
|
|
// API 호출
|
|
ModernDashboardApi.getSocketCombined(serverName, adapterGroup, function(data) {
|
|
if (data) {
|
|
// 기관 상태 데이터 표시
|
|
updateInstitutionStatusTable(data.institutionStatus);
|
|
|
|
// 기관 시각화 업데이트
|
|
updateInstitutionVisualization(data.institutionStatus);
|
|
|
|
// 회선 상태 데이터 표시
|
|
updateSocketStatusTable(data.socketStatus);
|
|
|
|
// 회선 연결 상태 데이터 표시
|
|
updateSocketProcessTable(data.socketProcess);
|
|
}
|
|
}, function(xhr, status, error) {
|
|
// 에러 처리
|
|
const errorMsg = '<div style="padding: 2rem; text-align: center; color: #dc3545;"><i class="fas fa-exclamation-circle" style="font-size: 3rem;"></i><p style="margin-top: 1rem;">데이터를 불러오는 중 오류가 발생했습니다.</p></div>';
|
|
|
|
institutionTable.innerHTML = '<tr><td colspan="6">' + errorMsg + '</td></tr>';
|
|
statusTable.innerHTML = '<tr><td colspan="7">' + errorMsg + '</td></tr>';
|
|
processTable.innerHTML = '<tr><td colspan="10">' + errorMsg + '</td></tr>';
|
|
institutionGrid.innerHTML = errorMsg;
|
|
console.error('소켓 데이터 로드 실패:', error);
|
|
});
|
|
}
|
|
|
|
// 기관 상태 테이블 업데이트
|
|
function updateInstitutionStatusTable(data) {
|
|
if (!data || data.length === 0) {
|
|
institutionTable.innerHTML = '<tr><td colspan="6"><div style="padding: 2rem; text-align: center; color: #6c757d;"><i class="fas fa-info-circle" style="font-size: 2rem;"></i><p style="margin-top: 1rem;">표시할 데이터가 없습니다.</p></div></td></tr>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
data.forEach(institution => {
|
|
let statusClass = '';
|
|
let statusText = '';
|
|
|
|
// 상태에 따른 텍스트 및 클래스 설정
|
|
if (institution.lineStatus === 'UNDEFINED') {
|
|
statusText = '미사용';
|
|
statusClass = 'status-badge status-badge-inactive';
|
|
} else if (institution.lineStatus === 'WARN') {
|
|
statusText = '경고';
|
|
statusClass = 'status-badge status-badge-warning';
|
|
} else if (institution.lineStatus === 'ERROR') {
|
|
statusText = '오류';
|
|
statusClass = 'status-badge status-badge-error';
|
|
} else {
|
|
statusText = '정상';
|
|
statusClass = 'status-badge status-badge-normal';
|
|
}
|
|
|
|
html += '<tr>' +
|
|
'<td>' + (institution.institutionName || '-') + '</td>' +
|
|
'<td>' + (institution.contactPerson || '-') + '</td>' +
|
|
'<td>' + (institution.contactNumber || '-') + '</td>' +
|
|
'<td>' + (institution.normalAdapterCount || '0') + '</td>' +
|
|
'<td>' + (institution.errorAdapterCount || '0') + '</td>' +
|
|
'<td><span class="' + statusClass + '">' + statusText + '</span></td>' +
|
|
'</tr>';
|
|
});
|
|
|
|
institutionTable.innerHTML = html;
|
|
}
|
|
|
|
// 기관 시각화 업데이트
|
|
function updateInstitutionVisualization(data) {
|
|
if (!data || data.length === 0) {
|
|
institutionGrid.innerHTML = '<div style="padding: 2rem; text-align: center; color: #6c757d;"><i class="fas fa-info-circle" style="font-size: 2rem;"></i><p style="margin-top: 1rem;">표시할 데이터가 없습니다.</p></div>';
|
|
updateStatsCounts(0, 0, 0, 0, 0);
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
let normalCount = 0;
|
|
let warningCount = 0;
|
|
let errorCount = 0;
|
|
let inactiveCount = 0;
|
|
|
|
data.forEach(institution => {
|
|
let statusClass = 'status-normal';
|
|
let statusIcon = '<i class="bi bi-check-circle" style="color: #28a745;"></i>';
|
|
let statusName = '정상';
|
|
|
|
// 상태에 따른 시각화 설정
|
|
if (institution.lineStatus === 'UNDEFINED') {
|
|
statusClass = 'status-inactive';
|
|
statusIcon = '<i class="bi bi-dash-circle" style="color: #6c757d;"></i>';
|
|
statusName = '미사용';
|
|
inactiveCount++;
|
|
} else if (institution.lineStatus === 'WARN') {
|
|
statusClass = 'status-warning';
|
|
statusIcon = '<i class="bi bi-exclamation-triangle" style="color: #ffc107;"></i>';
|
|
statusName = '경고';
|
|
warningCount++;
|
|
} else if (institution.lineStatus === 'ERROR') {
|
|
statusClass = 'status-error';
|
|
statusIcon = '<i class="bi bi-x-circle" style="color: #dc3545;"></i>';
|
|
statusName = '오류';
|
|
errorCount++;
|
|
} else {
|
|
normalCount++;
|
|
}
|
|
|
|
html += '<div class="institution-card ' + statusClass + '" data-code="' + institution.institutionCode + '">' +
|
|
'<div class="institution-status-indicator">' + statusIcon + '</div>' +
|
|
'<div class="institution-name">' + (institution.institutionName || '미지정 기관') + '</div>' +
|
|
'<div class="institution-info">' +
|
|
'<span class="institution-info-label">담당자:</span>' +
|
|
'<span>' + (institution.contactPerson || '-') + '</span>' +
|
|
'</div>' +
|
|
'<div class="institution-info">' +
|
|
'<span class="institution-info-label">연락처:</span>' +
|
|
'<span>' + (institution.contactNumber || '-') + '</span>' +
|
|
'</div>' +
|
|
'<div class="institution-info">' +
|
|
'<span class="institution-info-label">상태:</span>' +
|
|
'<span>' + statusName + '</span>' +
|
|
'</div>' +
|
|
'<div class="institution-stats">' +
|
|
'<div class="institution-stat">' +
|
|
'<div class="stat-number normal">' + (institution.normalAdapterCount || '0') + '</div>' +
|
|
'<div class="stat-label">정상 어댑터</div>' +
|
|
'</div>' +
|
|
'<div class="institution-stat">' +
|
|
'<div class="stat-number error">' + (institution.errorAdapterCount || '0') + '</div>' +
|
|
'<div class="stat-label">오류 어댑터</div>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'</div>';
|
|
});
|
|
|
|
institutionGrid.innerHTML = html;
|
|
updateStatsCounts(data.length, normalCount, warningCount, errorCount, inactiveCount);
|
|
|
|
// 기관 카드 클릭 이벤트 - 해당 기관 정보로 필터링
|
|
const institutionCards = institutionGrid.querySelectorAll('.institution-card');
|
|
institutionCards.forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
const institutionCode = card.getAttribute('data-code');
|
|
// 여기에 기관 코드로 필터링하는 로직 추가
|
|
console.log('기관 선택:', institutionCode);
|
|
|
|
// 기관에 해당하는 어댑터로 필터링하는 탭으로 이동
|
|
const socketStatusTab = document.querySelector('.tab-btn[data-tab="socket-status"]');
|
|
socketStatusTab.click();
|
|
});
|
|
});
|
|
}
|
|
|
|
// 통계 수치 업데이트
|
|
function updateStatsCounts(total, normal, warning, error, inactive) {
|
|
totalInstitutionsCount.textContent = total;
|
|
normalInstitutionsCount.textContent = normal;
|
|
warningInstitutionsCount.textContent = warning;
|
|
errorInstitutionsCount.textContent = error;
|
|
inactiveInstitutionsCount.textContent = inactive;
|
|
}
|
|
|
|
// 회선 상태 테이블 업데이트
|
|
function updateSocketStatusTable(data) {
|
|
if (!data || !data.rows || data.rows.length === 0) {
|
|
statusTable.innerHTML = '<tr><td colspan="7"><div style="padding: 2rem; text-align: center; color: #6c757d;"><i class="fas fa-info-circle" style="font-size: 2rem;"></i><p style="margin-top: 1rem;">표시할 데이터가 없습니다.</p></div></td></tr>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
data.rows.forEach(row => {
|
|
let statusClass = row.status === 'true' ? 'status-badge status-badge-normal' : 'status-badge status-badge-error';
|
|
const statusName = row.status === 'true' ? '정상' : '오류';
|
|
|
|
html += '<tr>' +
|
|
'<td>' + (row.adptrBzwkGroupName || '-') + '</td>' +
|
|
'<td>' + (row.eaiSevrInstncName || '-') + '</td>' +
|
|
'<td>' + (row.adptrNickName || '-') + '</td>' +
|
|
'<td>' + (row.adptrBzwkDesc || '-') + '</td>' +
|
|
'<td>' + (row.adptrType || '-') + '</td>' +
|
|
'<td><span class="' + statusClass + '">' + statusName + '</span></td>' +
|
|
'<td>' + (row.starting || '-') + '</td>' +
|
|
'</tr>';
|
|
});
|
|
|
|
statusTable.innerHTML = html;
|
|
}
|
|
|
|
// 회선 연결 상태 테이블 업데이트
|
|
function updateSocketProcessTable(data) {
|
|
if (!data || !data.rows || data.rows.length === 0) {
|
|
processTable.innerHTML = '<tr><td colspan="10"><div style="padding: 2rem; text-align: center; color: #6c757d;"><i class="fas fa-info-circle" style="font-size: 2rem;"></i><p style="margin-top: 1rem;">표시할 데이터가 없습니다.</p></div></td></tr>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
data.rows.forEach(row => {
|
|
let statusClass = row.status === 'true' ? 'status-badge status-badge-normal' : 'status-badge status-badge-error';
|
|
const statusName = row.status === 'true' ? '정상' : '오류';
|
|
|
|
html += '<tr>' +
|
|
'<td>' + (row.adptrBzwkGroupName || '-') + '</td>' +
|
|
'<td>' + (row.eaiSevrInstncName || '-') + '</td>' +
|
|
'<td>' + (row.adptrBzwkName || '-') + '</td>' +
|
|
'<td>' + (row.adptrBzwkDesc || '-') + '</td>' +
|
|
'<td>' + (row.maxConnection || '0') + '</td>' +
|
|
'<td>' + (row.currentConnection || '0') + '</td>' +
|
|
'<td>' + (row.sendCount || '0') + '</td>' +
|
|
'<td>' + (row.recvCount || '0') + '</td>' +
|
|
'<td>' + (row.errorCount || '0') + '</td>' +
|
|
'<td><span class="' + statusClass + '">' + statusName + '</span></td>' +
|
|
'</tr>';
|
|
});
|
|
|
|
processTable.innerHTML = html;
|
|
}
|
|
|
|
// 모달 열기 함수 (전역 스코프에 노출)
|
|
window.openSocketDetailModal = function(serverName, adapterGroup) {
|
|
// 모달 표시
|
|
modal.style.display = 'block';
|
|
|
|
// 데이터 로드
|
|
loadSocketData(serverName, adapterGroup);
|
|
};
|
|
})();
|
|
</script> |