모던 대시보드 패치
- ApexCharts 기반 실시간 모니터링 대시보드 - 어댑터/배치/서버/소켓/트랜잭션 모니터링 컴포넌트 - 처리량 계산 및 데이터 매핑 서비스
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
||||
<%@ page import="java.util.*" %>
|
||||
<%@ page import="com.eactive.eai.rms.common.datasource.*" %>
|
||||
|
||||
<div class="dashboard-adapter-summary">
|
||||
<div class="dashboard-header">
|
||||
<div class="header-title-container">
|
||||
<h2 class="section-title">회선 상태</h2>
|
||||
<%--<div class="adapter-actions">
|
||||
<button class="filter-btn view-socket-detail active">상세보기</button>
|
||||
</div>--%>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card adapter-summary-card">
|
||||
<div class="adapter-summary-container">
|
||||
<div class="adapter-summary-header">
|
||||
<div class="adapter-type-column">어댑터 유형</div>
|
||||
<div class="adapter-status-column">
|
||||
<div class="status-label error-label">에러</div>
|
||||
<div class="status-label warning-label">경고</div>
|
||||
<div class="status-label normal-label">정상</div>
|
||||
<div class="status-label inactive-label">정지</div>
|
||||
<div class="status-label total-label">합계</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="adapter-summary-body">
|
||||
<!-- 어댑터 요약 데이터가 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 어댑터 요약 행 템플릿 -->
|
||||
<template id="adapter-summary-row-template">
|
||||
<div class="adapter-summary-row">
|
||||
<div class="adapter-type-column">
|
||||
<div class="adapter-type-info">
|
||||
<span class="adapter-type-name">Socket</span>
|
||||
<span class="status-badge adapter-status-normal">정상</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="adapter-status-column">
|
||||
<div class="status-count error-count">0</div>
|
||||
<div class="status-count warning-count">0</div>
|
||||
<div class="status-count normal-count">0</div>
|
||||
<div class="status-count inactive-count">0</div>
|
||||
<div class="status-count total-count">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 소켓 상세 정보 모달 포함 -->
|
||||
<jsp:include page="dashboard-socket-detail.jsp" />
|
||||
|
||||
<style>
|
||||
/* 어댑터 섹션 */
|
||||
.adapter-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.adapter-table-container {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 어댑터 탭 스타일 개선 */
|
||||
.dashboard-adapters .card-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
background-color: var(--color-gray-50);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.dashboard-adapters .tab-btn {
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-gray-600);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.dashboard-adapters .tab-btn:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dashboard-adapters .tab-btn.active {
|
||||
color: var(--color-primary);
|
||||
border-bottom-color: var(--color-primary);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.dashboard-adapters .tab-content {
|
||||
display: none;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.dashboard-adapters .tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 어댑터 섹션 스타일 조정 */
|
||||
.adapters-container .card {
|
||||
height: 100%;
|
||||
min-height: 249px;
|
||||
}
|
||||
|
||||
.dashboard-adapter-summary {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.adapter-summary-card {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.adapter-summary-container {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.adapter-summary-header {
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding-bottom: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.adapter-summary-body {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.adapter-summary-row {
|
||||
display: flex;
|
||||
padding: 0.25rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.adapter-summary-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.adapter-type-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adapter-type-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adapter-type-name {
|
||||
font-weight: bold;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.adapter-status-column {
|
||||
flex: 3;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-label, .status-count {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.adapter-status-normal {
|
||||
background-color: rgba(76, 175, 80, 0.15);
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.adapter-status-warning {
|
||||
background-color: rgba(255, 152, 0, 0.15);
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
.adapter-status-error {
|
||||
background-color: rgba(229, 57, 53, 0.15);
|
||||
color: #e53935;
|
||||
}
|
||||
|
||||
.adapter-status-inactive {
|
||||
background-color: rgba(158, 158, 158, 0.15);
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
.error-label, .error-count {
|
||||
color: #e53935;
|
||||
}
|
||||
|
||||
.warning-label, .warning-count {
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
.normal-label, .normal-count {
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.inactive-label, .inactive-count {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
.total-label, .total-count {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.empty-data-message {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
color: #757575;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 어댑터 헤더 스타일 */
|
||||
.adapter-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* 대시보드 헤더 스타일 */
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
/* 제목과 버튼을 한 줄에 나란히 표시하기 위한 컨테이너 */
|
||||
.header-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adapter-actions {
|
||||
margin-left: 10px; /* 제목과 버튼 사이 간격 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background-color: var(--color-gray-200);
|
||||
color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #0069d9;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// 어댑터 요약 행 생성 함수
|
||||
function createAdapterSummaryRow(adapterSummary) {
|
||||
var template = document.getElementById('adapter-summary-row-template');
|
||||
var clone = document.importNode(template.content, true);
|
||||
|
||||
// 어댑터 유형 설정
|
||||
clone.querySelector('.adapter-type-name').textContent = adapterSummary.adapterType || '—';
|
||||
|
||||
// 상태 설정
|
||||
var statusBadge = clone.querySelector('.status-badge');
|
||||
statusBadge.className = 'status-badge';
|
||||
|
||||
if (adapterSummary.overallStatus === 'normal') {
|
||||
statusBadge.classList.add('adapter-status-normal');
|
||||
statusBadge.textContent = '정상';
|
||||
} else if (adapterSummary.overallStatus === 'warning') {
|
||||
statusBadge.classList.add('adapter-status-warning');
|
||||
statusBadge.textContent = '경고';
|
||||
} else if (adapterSummary.overallStatus === 'error') {
|
||||
statusBadge.classList.add('adapter-status-error');
|
||||
statusBadge.textContent = '오류';
|
||||
} else if (adapterSummary.overallStatus === 'inactive') {
|
||||
statusBadge.classList.add('adapter-status-inactive');
|
||||
statusBadge.textContent = '정지';
|
||||
} else {
|
||||
statusBadge.classList.add('adapter-status-inactive');
|
||||
statusBadge.textContent = '알 수 없음';
|
||||
}
|
||||
|
||||
// 상태별 카운트 설정
|
||||
clone.querySelector('.error-count').textContent = formatNumber(adapterSummary.errorCount || 0);
|
||||
clone.querySelector('.warning-count').textContent = formatNumber(adapterSummary.warningCount || 0);
|
||||
clone.querySelector('.normal-count').textContent = formatNumber(adapterSummary.normalCount || 0);
|
||||
clone.querySelector('.inactive-count').textContent = formatNumber(adapterSummary.inactiveCount || 0);
|
||||
clone.querySelector('.total-count').textContent = formatNumber(adapterSummary.totalCount || 0);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
// 어댑터 요약 테이블 업데이트 함수
|
||||
function updateAdapterSummaryTable(data) {
|
||||
if (!data || !data.serverAdapters) return;
|
||||
|
||||
// 어댑터 요약 컨테이너 찾기
|
||||
var summaryBody = document.querySelector('.adapter-summary-body');
|
||||
if (!summaryBody) return;
|
||||
|
||||
// 컨테이너 초기화
|
||||
summaryBody.innerHTML = '';
|
||||
|
||||
// 모든 서버의 어댑터 요약 데이터 통합
|
||||
var allAdapterSummaries = [];
|
||||
data.serverAdapters.forEach(function(serverAdapter) {
|
||||
if (serverAdapter.adapterSummaries && serverAdapter.adapterSummaries.length > 0) {
|
||||
allAdapterSummaries = allAdapterSummaries.concat(serverAdapter.adapterSummaries);
|
||||
}
|
||||
});
|
||||
|
||||
// 어댑터 유형별로 그룹화
|
||||
var adapterTypeMap = {};
|
||||
allAdapterSummaries.forEach(function(summary) {
|
||||
if (!adapterTypeMap[summary.adapterType]) {
|
||||
adapterTypeMap[summary.adapterType] = summary;
|
||||
} else {
|
||||
// 기존 데이터에 카운트 누적
|
||||
var existing = adapterTypeMap[summary.adapterType];
|
||||
existing.errorCount = (existing.errorCount || 0) + (summary.errorCount || 0);
|
||||
existing.warningCount = (existing.warningCount || 0) + (summary.warningCount || 0);
|
||||
existing.normalCount = (existing.normalCount || 0) + (summary.normalCount || 0);
|
||||
existing.inactiveCount = (existing.inactiveCount || 0) + (summary.inactiveCount || 0);
|
||||
existing.totalCount = (existing.totalCount || 0) + (summary.totalCount || 0);
|
||||
|
||||
// 전체 상태 업데이트
|
||||
if (summary.overallStatus === 'error' || existing.overallStatus === 'error') {
|
||||
existing.overallStatus = 'error';
|
||||
} else if (summary.overallStatus === 'warning' || existing.overallStatus === 'warning') {
|
||||
existing.overallStatus = 'warning';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 필터링된 어댑터 유형만 표시 (Socket, Socket2, HTTP)
|
||||
var filteredAdapterTypes = Object.keys(adapterTypeMap).filter(function(type) {
|
||||
return type === 'Socket' || type === 'Socket2' || type === 'HTTP';
|
||||
});
|
||||
|
||||
if (filteredAdapterTypes.length > 0) {
|
||||
// 어댑터 유형별로 행 추가
|
||||
filteredAdapterTypes.forEach(function(type) {
|
||||
var summaryRow = createAdapterSummaryRow(adapterTypeMap[type]);
|
||||
summaryBody.appendChild(summaryRow);
|
||||
});
|
||||
} else {
|
||||
// 데이터가 없는 경우 안내 메시지 표시
|
||||
var emptyRow = document.createElement('div');
|
||||
emptyRow.className = 'empty-data-message';
|
||||
emptyRow.textContent = '표시할 어댑터 데이터가 없습니다.';
|
||||
summaryBody.appendChild(emptyRow);
|
||||
}
|
||||
}
|
||||
|
||||
// 어댑터 요약 데이터 로드 함수
|
||||
function loadAdapterSummaryData() {
|
||||
// 어댑터 요약 데이터 API 호출
|
||||
ModernDashboardApi.getAdapterStatus(null, function(data) {
|
||||
if (data) {
|
||||
updateAdapterSummaryTable(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 문서 로드 완료 시 초기화
|
||||
$(document).ready(function() {
|
||||
// 초기 데이터 로드
|
||||
loadAdapterSummaryData();
|
||||
|
||||
// 소켓 어댑터 상세 보기 버튼 이벤트
|
||||
$('.view-socket-detail').on('click', function() {
|
||||
// 소켓 상세 정보 모달 열기
|
||||
openSocketDetailModal();
|
||||
});
|
||||
|
||||
// DashboardEventManager를 사용하여 새로고침 이벤트 구독
|
||||
if (window.DashboardEventManager) {
|
||||
window.DashboardEventManager.on('refresh', function() {
|
||||
// 어댑터 요약 데이터 새로고침
|
||||
loadAdapterSummaryData();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user