9545d69dc4
존재하지 않는 DB 테이블을 참조하여 발생하던 대시보드 오류를 해결. K8S 환경 확인을 위한 DB 조회 로직을 제거, 일반 환경에서 항상 정상 동작하도록 수정.
714 lines
20 KiB
Plaintext
714 lines
20 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
|
|
|
<div class="dashboard-summary">
|
|
<h2 class="section-title">실시간 트랜잭션 현황</h2>
|
|
<div class="card">
|
|
<div class="chart-controls">
|
|
<div class="btn-group" style="display: none">
|
|
<button class="btn btn-chart-type" data-type="area">영역</button>
|
|
<button class="btn btn-chart-type active" data-type="line">선형</button>
|
|
<button class="btn btn-chart-type" data-type="bar">막대</button>
|
|
</div>
|
|
<div class="btn-group ml-2">
|
|
<button class="btn btn-time-range" data-range="5m">5분</button>
|
|
<button class="btn btn-time-range active" data-range="10m">10분</button>
|
|
<button class="btn btn-time-range" data-range="30m">30분</button>
|
|
<button class="btn btn-time-range" data-range="1h">1시간</button>
|
|
</div>
|
|
</div>
|
|
<div class="chart-container">
|
|
<div id="transaction-chart" class="chart-content">
|
|
<i class="bi bi-bar-chart-line"></i>
|
|
<span>트랜잭션 데이터를 로딩 중입니다...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ApexCharts 라이브러리 로드 -->
|
|
<script src="<%=request.getContextPath()%>/addon/apexcharts/apexcharts.js"></script>
|
|
|
|
<!-- 트랜잭션 차트 관련 스크립트 -->
|
|
<script>
|
|
// 트랜잭션 시계열 데이터 관리 객체
|
|
const TransactionTimeSeriesData = {
|
|
// 서비스별 시계열 데이터를 저장할 맵
|
|
serviceData: {},
|
|
|
|
// 최대 데이터 포인트 수 (60분 간격, 10초마다 갱신 = 360개 포인트)
|
|
maxDataPoints: 360,
|
|
|
|
// 초기화
|
|
init: function() {
|
|
// 서비스 목록 초기화
|
|
this.initServiceTypes();
|
|
},
|
|
|
|
// 서비스 유형 초기화
|
|
initServiceTypes: function() {
|
|
// 서버 데이터 배열에서 서비스 타입 추출
|
|
if (window.serverDataArray) {
|
|
const uniqueTypes = new Set();
|
|
serverDataArray.forEach(server => {
|
|
uniqueTypes.add(server.type);
|
|
});
|
|
|
|
// 각 고유 서비스 타입에 대해 시계열 데이터 초기화
|
|
uniqueTypes.forEach(serviceType => {
|
|
if (!this.serviceData[serviceType]) {
|
|
this.serviceData[serviceType] = {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
}
|
|
});
|
|
} else {
|
|
// 기본 서비스 타입 (기본값)
|
|
const defaultTypes = ['EAI', 'FEP', 'MCI', 'MCU', 'BAP'];
|
|
defaultTypes.forEach(serviceType => {
|
|
this.serviceData[serviceType] = {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
});
|
|
}
|
|
|
|
// 합계 데이터를 위한 TOTAL 서비스 추가
|
|
this.serviceData['TOTAL'] = {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
},
|
|
|
|
// 타임스탬프를 Date 객체로 변환
|
|
ensureDateObject: function(timestamp) {
|
|
if (timestamp instanceof Date) {
|
|
return timestamp;
|
|
} else if (typeof timestamp === 'number') {
|
|
return new Date(timestamp);
|
|
} else if (typeof timestamp === 'string') {
|
|
return new Date(timestamp);
|
|
}
|
|
return new Date(); // 기본값으로 현재 시간 반환
|
|
},
|
|
|
|
// 모의 데이터 생성 (테스트용)
|
|
generateMockData: function() {
|
|
const mockData = [];
|
|
for (const serviceType in this.serviceData) {
|
|
mockData.push({
|
|
type: serviceType,
|
|
success: 0,
|
|
fail: 0,
|
|
timeout: 0,
|
|
syserror: 0
|
|
});
|
|
}
|
|
return mockData;
|
|
},
|
|
|
|
// 트랜잭션 데이터 업데이트
|
|
updateData: function(transactions) {
|
|
if (!transactions || !transactions.length) return;
|
|
|
|
const timestamp = new Date();
|
|
|
|
// 각 서비스별 데이터 업데이트
|
|
transactions.forEach(transaction => {
|
|
const serviceType = transaction.type;
|
|
|
|
// 해당 서비스의 시계열 데이터가 없으면 초기화
|
|
if (!this.serviceData[serviceType]) {
|
|
this.serviceData[serviceType] = {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
}
|
|
|
|
const data = this.serviceData[serviceType];
|
|
|
|
// 타임스탬프 추가
|
|
data.timestamps.push(timestamp);
|
|
|
|
// 데이터 추가
|
|
data.success.push(transaction.success || 0);
|
|
data.fail.push(transaction.fail || 0);
|
|
data.timeout.push(transaction.timeout || 0);
|
|
data.syserror.push(transaction.syserror || 0);
|
|
|
|
// 최대 데이터 포인트 수 제한
|
|
if (data.timestamps.length > this.maxDataPoints) {
|
|
data.timestamps.shift();
|
|
data.success.shift();
|
|
data.fail.shift();
|
|
data.timeout.shift();
|
|
data.syserror.shift();
|
|
}
|
|
});
|
|
},
|
|
|
|
// 오래된 데이터 정리 (60분 이상 경과된 데이터)
|
|
cleanupData: function() {
|
|
const cutoffTime = new Date(new Date() - 60 * 60 * 1000); // 60분 전
|
|
|
|
for (const serviceType in this.serviceData) {
|
|
const data = this.serviceData[serviceType];
|
|
|
|
// 각 데이터 시리즈에서 60분 이전 데이터 제거
|
|
let i = 0;
|
|
while (i < data.timestamps.length && this.ensureDateObject(data.timestamps[i]) < cutoffTime) {
|
|
i++;
|
|
}
|
|
|
|
if (i > 0) {
|
|
data.timestamps = data.timestamps.slice(i);
|
|
data.success = data.success.slice(i);
|
|
data.fail = data.fail.slice(i);
|
|
data.timeout = data.timeout.slice(i);
|
|
data.syserror = data.syserror.slice(i);
|
|
}
|
|
}
|
|
},
|
|
|
|
// 특정 서비스의 시계열 데이터 가져오기
|
|
getServiceData: function(serviceType) {
|
|
return this.serviceData[serviceType] || {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
},
|
|
};
|
|
|
|
// 트랜잭션 차트 관리 객체
|
|
const TransactionChart = {
|
|
chart: null,
|
|
chartType: 'line', // 기본값: 영역 차트
|
|
timeRange: '10m', // 기본값: 5분
|
|
|
|
// 초기화
|
|
init: function(containerId) {
|
|
// ApexCharts 로드 확인
|
|
if (typeof ApexCharts === 'undefined') {
|
|
console.error('ApexCharts 라이브러리가 로드되지 않았습니다.');
|
|
return;
|
|
}
|
|
|
|
const container = document.getElementById(containerId);
|
|
if (!container) {
|
|
console.error('차트 컨테이너를 찾을 수 없습니다: ' + containerId);
|
|
return;
|
|
}
|
|
|
|
// 기존 차트 제거
|
|
if (this.chart) {
|
|
this.chart.destroy();
|
|
}
|
|
|
|
// 새 차트 인스턴스 생성
|
|
const options = this.getChartOptions();
|
|
this.chart = new ApexCharts(container, options);
|
|
this.chart.render();
|
|
|
|
// 브라우저 창 크기 변경 시 차트 크기 조정
|
|
window.addEventListener('resize', () => {
|
|
if (this.chart) {
|
|
this.chart.updateOptions({
|
|
chart: {
|
|
width: '100%'
|
|
}
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 차트 옵션 가져오기
|
|
getChartOptions: function() {
|
|
return {
|
|
chart: {
|
|
type: this.chartType,
|
|
height: 180,
|
|
toolbar: {
|
|
show: false
|
|
},
|
|
animations: {
|
|
enabled: true,
|
|
easing: 'easeinout',
|
|
speed: 800,
|
|
animateGradually: {
|
|
enabled: true,
|
|
delay: 150
|
|
},
|
|
dynamicAnimation: {
|
|
enabled: true,
|
|
speed: 350
|
|
}
|
|
},
|
|
fontFamily: 'Noto Sans KR, sans-serif',
|
|
zoom: {
|
|
enabled: false
|
|
}
|
|
},
|
|
dataLabels: {
|
|
enabled: false
|
|
},
|
|
stroke: {
|
|
curve: 'smooth',
|
|
width: 2
|
|
},
|
|
series: [],
|
|
colors: ['#4CAF50', '#F44336', '#2196F3', '#FF5722', '#9C27B0', '#FF9800'],
|
|
fill: {
|
|
type: this.chartType === 'area' ? 'gradient' : 'solid',
|
|
gradient: {
|
|
shade: 'light',
|
|
type: 'vertical',
|
|
shadeIntensity: 0.3,
|
|
opacityFrom: 0.7,
|
|
opacityTo: 0.2,
|
|
stops: [0, 90, 100]
|
|
}
|
|
},
|
|
grid: {
|
|
borderColor: '#e0e0e0',
|
|
strokeDashArray: 3,
|
|
xaxis: {
|
|
lines: {
|
|
show: false
|
|
}
|
|
}
|
|
},
|
|
tooltip: {
|
|
shared: true,
|
|
intersect: false,
|
|
y: {
|
|
formatter: function(value) {
|
|
return value.toLocaleString() + ' 건';
|
|
}
|
|
},
|
|
x: {
|
|
formatter: function(value) {
|
|
const date = new Date(value);
|
|
return date.toLocaleTimeString();
|
|
}
|
|
}
|
|
},
|
|
xaxis: {
|
|
type: 'datetime',
|
|
labels: {
|
|
datetimeUTC: false,
|
|
format: 'HH:mm'
|
|
},
|
|
tooltip: {
|
|
enabled: false
|
|
},
|
|
tickAmount: 6
|
|
},
|
|
yaxis: {
|
|
title: {
|
|
text: '처리 건수',
|
|
style: {
|
|
fontWeight: 600
|
|
}
|
|
},
|
|
labels: {
|
|
formatter: function(value) {
|
|
return value.toLocaleString();
|
|
}
|
|
},
|
|
forceNiceScale: true,
|
|
min: undefined,
|
|
max: undefined
|
|
},
|
|
legend: {
|
|
position: 'top',
|
|
horizontalAlign: 'right',
|
|
floating: true,
|
|
offsetY: -25,
|
|
offsetX: -5,
|
|
showForSingleSeries: true,
|
|
onItemClick: {
|
|
toggleDataSeries: true
|
|
},
|
|
onItemHover: {
|
|
highlightDataSeries: true
|
|
},
|
|
markers: {
|
|
width: 12,
|
|
height: 12,
|
|
strokeWidth: 0,
|
|
radius: 12,
|
|
offsetX: -5
|
|
},
|
|
itemMargin: {
|
|
horizontal: 10,
|
|
vertical: 5
|
|
}
|
|
},
|
|
states: {
|
|
hover: {
|
|
filter: {
|
|
type: 'lighten',
|
|
value: 0.05
|
|
}
|
|
},
|
|
active: {
|
|
filter: {
|
|
type: 'darken',
|
|
value: 0.1
|
|
}
|
|
}
|
|
},
|
|
responsive: [
|
|
{
|
|
breakpoint: 768,
|
|
options: {
|
|
legend: {
|
|
position: 'bottom',
|
|
horizontalAlign: 'center',
|
|
floating: false,
|
|
offsetY: 0,
|
|
offsetX: 0
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
},
|
|
|
|
// 서비스 타입에 따른 색상 맵
|
|
colorMap: {
|
|
'EAI': { success: '#4CAF50', error: '#F44336' },
|
|
'FEP': { success: '#2196F3', error: '#FF5722' },
|
|
'MCI': { success: '#9C27B0', error: '#FF9800' },
|
|
'MCU': { success: '#3F51B5', error: '#FFC107' },
|
|
'BAP': { success: '#00BCD4', error: '#795548' },
|
|
'TOTAL': { success: '#607D8B', error: '#9E9E9E' }
|
|
},
|
|
|
|
// 서비스 및 데이터 타입에 따른 색상 반환
|
|
getServiceColor: function(serviceType, dataType) {
|
|
if (this.colorMap[serviceType] && this.colorMap[serviceType][dataType]) {
|
|
return this.colorMap[serviceType][dataType];
|
|
}
|
|
|
|
// 기본 색상
|
|
return dataType === 'success' ? '#66BB6A' : '#EF5350';
|
|
},
|
|
|
|
// 차트 타입 설정
|
|
setChartType: function(type) {
|
|
this.chartType = type;
|
|
this.updateChart();
|
|
},
|
|
|
|
// 시간 범위 설정
|
|
setTimeRange: function(range) {
|
|
this.timeRange = range;
|
|
this.updateChart();
|
|
},
|
|
|
|
// 차트 업데이트
|
|
updateChart: function() {
|
|
if (!this.chart) return;
|
|
|
|
const series = [];
|
|
|
|
// 각 서비스에 대한 시리즈 생성 (TOTAL 제외)
|
|
for (const serviceType in TransactionTimeSeriesData.serviceData) {
|
|
if (serviceType === 'TOTAL') continue;
|
|
|
|
const serviceData = TransactionTimeSeriesData.serviceData[serviceType];
|
|
if (!serviceData.timestamps.length) continue;
|
|
|
|
// 성공 시리즈
|
|
series.push({
|
|
name: serviceType + ' 성공',
|
|
type: this.chartType,
|
|
data: serviceData.timestamps.map((time, index) => ({
|
|
x: TransactionTimeSeriesData.ensureDateObject(time).getTime(),
|
|
y: serviceData.success[index]
|
|
})),
|
|
color: this.getServiceColor(serviceType, 'success')
|
|
});
|
|
|
|
// 에러 시리즈 (실패 + 타임아웃 + 시스템 에러)
|
|
series.push({
|
|
name: serviceType + ' 에러',
|
|
type: this.chartType,
|
|
data: serviceData.timestamps.map((time, index) => ({
|
|
x: TransactionTimeSeriesData.ensureDateObject(time).getTime(),
|
|
y: serviceData.fail[index] + serviceData.timeout[index] + serviceData.syserror[index]
|
|
})),
|
|
color: this.getServiceColor(serviceType, 'error')
|
|
});
|
|
}
|
|
|
|
// 시간 범위에 따른 X축 범위 설정
|
|
const rangeMinutes = {
|
|
'5m': 5,
|
|
'10m': 10,
|
|
'30m': 30,
|
|
'1h': 60
|
|
}[this.timeRange] || 10;
|
|
|
|
const startTime = new Date(new Date() - rangeMinutes * 60 * 1000);
|
|
const endTime = new Date();
|
|
|
|
// 차트 옵션 업데이트 - 전체 다시 그리기 방지
|
|
this.chart.updateOptions({
|
|
chart: {
|
|
type: this.chartType
|
|
},
|
|
fill: {
|
|
type: this.chartType === 'area' ? 'gradient' : 'solid'
|
|
},
|
|
xaxis: {
|
|
min: startTime.getTime(),
|
|
max: endTime.getTime() + 1000
|
|
}
|
|
}, false, false); // 차트 다시 그리지 않고 업데이트
|
|
|
|
// 시리즈 데이터만 업데이트
|
|
this.chart.updateSeries(series, false);
|
|
},
|
|
|
|
// 모의 데이터로 차트 업데이트 (테스트용)
|
|
updateWithMockData: function() {
|
|
const mockData = TransactionTimeSeriesData.generateMockData();
|
|
TransactionTimeSeriesData.updateData(mockData);
|
|
this.updateChart();
|
|
}
|
|
};
|
|
|
|
// 문서 로드 완료 시 초기화
|
|
$(document).ready(function() {
|
|
// 시계열 데이터 관리 객체 초기화
|
|
TransactionTimeSeriesData.init();
|
|
|
|
// 차트 초기화
|
|
TransactionChart.init('transaction-chart');
|
|
|
|
// 차트 컨트롤 이벤트 설정
|
|
setupChartControls();
|
|
|
|
// 초기 데이터 로드
|
|
loadInitialChartData();
|
|
|
|
// 10초마다 오래된 데이터 정리
|
|
setInterval(() => TransactionTimeSeriesData.cleanupData(), 10000);
|
|
|
|
// DashboardEventManager를 사용하여 새로고침 이벤트 구독
|
|
if (window.DashboardEventManager) {
|
|
window.DashboardEventManager.on('refresh', function() {
|
|
// 트랜잭션 데이터 추가 로드
|
|
loadSummaryTransactionData();
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
// 차트 컨트롤 이벤트 설정
|
|
function setupChartControls() {
|
|
// 차트 타입 변경
|
|
$('.btn-chart-type').click(function() {
|
|
// 기존 활성화 버튼 비활성화
|
|
$('.btn-chart-type').removeClass('active');
|
|
|
|
// 클릭한 버튼 활성화
|
|
$(this).addClass('active');
|
|
|
|
// 차트 타입 변경
|
|
const chartType = $(this).data('type');
|
|
TransactionChart.setChartType(chartType);
|
|
});
|
|
|
|
// 시간 범위 변경
|
|
$('.btn-time-range').click(function() {
|
|
// 기존 활성화 버튼 비활성화
|
|
$('.btn-time-range').removeClass('active');
|
|
|
|
// 클릭한 버튼 활성화
|
|
$(this).addClass('active');
|
|
|
|
// 시간 범위 변경
|
|
const timeRange = $(this).data('range');
|
|
TransactionChart.setTimeRange(timeRange);
|
|
});
|
|
}
|
|
|
|
// 초기 차트 데이터 로드
|
|
function loadInitialChartData() {
|
|
// 시계열 데이터 API 호출
|
|
ModernDashboardApi.getTransactionTimeSeries(null, 10, function(data) {
|
|
if (data && data.timeSeriesData) {
|
|
// 각 서비스별 시계열 데이터 처리
|
|
for (var serviceType in data.timeSeriesData) {
|
|
var serviceData = data.timeSeriesData[serviceType];
|
|
if (!serviceData.timestamps || serviceData.timestamps.length === 0) continue;
|
|
|
|
// 기존 데이터 초기화
|
|
TransactionTimeSeriesData.serviceData[serviceType] = {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
|
|
// API 응답 데이터 복사 및 타임스탬프 변환
|
|
TransactionTimeSeriesData.serviceData[serviceType].timestamps = serviceData.timestamps.map(time =>
|
|
TransactionTimeSeriesData.ensureDateObject(time)
|
|
);
|
|
TransactionTimeSeriesData.serviceData[serviceType].success = serviceData.success.slice();
|
|
TransactionTimeSeriesData.serviceData[serviceType].fail = serviceData.fail.slice();
|
|
TransactionTimeSeriesData.serviceData[serviceType].timeout = serviceData.timeout.slice();
|
|
TransactionTimeSeriesData.serviceData[serviceType].syserror = serviceData.syserror.slice();
|
|
}
|
|
|
|
// 차트 업데이트
|
|
TransactionChart.updateChart();
|
|
} else {
|
|
// API 응답이 없는 경우 모의 데이터로 테스트
|
|
TransactionChart.updateWithMockData();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 트랜잭션 데이터 로드 및 차트 업데이트 (함수명 변경)
|
|
function loadSummaryTransactionData() {
|
|
|
|
// 현재 선택된 시간 범위 가져오기
|
|
var activeRange = $('.btn-time-range.active').data('range') || '10m';
|
|
var minutes = {
|
|
'5m': 5,
|
|
'10m': 10,
|
|
'30m': 30,
|
|
'1h': 60
|
|
}[activeRange] || 10;
|
|
|
|
ModernDashboardApi.getTransactionTimeSeries(null, minutes, function(data) {
|
|
if (data && data.timeSeriesData) {
|
|
// 각 서비스별 시계열 데이터 처리
|
|
for (var serviceType in data.timeSeriesData) {
|
|
var serviceData = data.timeSeriesData[serviceType];
|
|
if (!serviceData.timestamps || serviceData.timestamps.length === 0) continue;
|
|
|
|
// 기존 데이터 초기화
|
|
TransactionTimeSeriesData.serviceData[serviceType] = {
|
|
timestamps: [],
|
|
success: [],
|
|
fail: [],
|
|
timeout: [],
|
|
syserror: []
|
|
};
|
|
|
|
// API 응답 데이터 복사 및 타임스탬프 변환
|
|
TransactionTimeSeriesData.serviceData[serviceType].timestamps = serviceData.timestamps.map(time =>
|
|
TransactionTimeSeriesData.ensureDateObject(time)
|
|
);
|
|
TransactionTimeSeriesData.serviceData[serviceType].success = serviceData.success.slice();
|
|
TransactionTimeSeriesData.serviceData[serviceType].fail = serviceData.fail.slice();
|
|
TransactionTimeSeriesData.serviceData[serviceType].timeout = serviceData.timeout.slice();
|
|
TransactionTimeSeriesData.serviceData[serviceType].syserror = serviceData.syserror.slice();
|
|
}
|
|
|
|
// 차트 업데이트 - 부드러운 업데이트
|
|
TransactionChart.updateChart();
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<!-- 추가 CSS 스타일 -->
|
|
<style>
|
|
.chart-controls {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.chart-controls .btn-group {
|
|
display: flex;
|
|
}
|
|
|
|
.chart-controls .btn {
|
|
padding: 4px 8px;
|
|
font-size: 0.75rem;
|
|
border: 1px solid #e0e0e0;
|
|
background-color: #fff;
|
|
color: #666;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.chart-controls .btn:first-child {
|
|
border-top-left-radius: 4px;
|
|
border-bottom-left-radius: 4px;
|
|
}
|
|
|
|
.chart-controls .btn:last-child {
|
|
border-top-right-radius: 4px;
|
|
border-bottom-right-radius: 4px;
|
|
}
|
|
|
|
.chart-controls .btn.active {
|
|
background-color: #2196F3;
|
|
color: white;
|
|
border-color: #2196F3;
|
|
}
|
|
|
|
.chart-controls .ml-2 {
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
.chart-content {
|
|
width: 100%;
|
|
height: 100%;
|
|
min-height: 200px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: var(--color-gray-400);
|
|
}
|
|
|
|
.card {
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
background-color: #fff;
|
|
padding: 16px;
|
|
margin-bottom: 0px;
|
|
}
|
|
|
|
/* 범례 스타일 개선 */
|
|
.apexcharts-legend-series {
|
|
margin: 3px 8px !important;
|
|
cursor: pointer !important;
|
|
}
|
|
|
|
.apexcharts-legend-text {
|
|
font-size: 0.85rem !important;
|
|
color: #333 !important;
|
|
font-weight: 500 !important;
|
|
}
|
|
|
|
.apexcharts-legend-marker {
|
|
margin-right: 5px !important;
|
|
}
|
|
|
|
.apexcharts-legend-series.apexcharts-inactive-legend {
|
|
opacity: 0.5 !important;
|
|
}
|
|
</style> |