491 lines
25 KiB
PHP
491 lines
25 KiB
PHP
<x-filament-widgets::widget wire:poll.10s="refreshData">
|
|
@php
|
|
$data = $this->getData();
|
|
$cpu = $data['cpu'] ?? [];
|
|
$memory = $data['memory'] ?? [];
|
|
$disk = $data['disk'] ?? [];
|
|
$partitions = $disk['partitions'] ?? [];
|
|
$load = $data['load'] ?? [];
|
|
$uptime = $data['uptime'] ?? 'N/A';
|
|
|
|
$cpuUsage = $cpu['usage'] ?? 0;
|
|
$memUsage = $memory['usage'] ?? 0;
|
|
// Memory values are in MB from agent
|
|
$memUsedGB = ($memory['used'] ?? 0) / 1024;
|
|
$memTotalGB = ($memory['total'] ?? 0) / 1024;
|
|
|
|
$partition = $partitions[0] ?? null;
|
|
$diskUsage = $partition ? round($partition['usage_percent'] ?? 0, 1) : 0;
|
|
$mountPoint = $partition['mount'] ?? '/';
|
|
$usedHuman = $partition['used_human'] ?? '0 B';
|
|
$totalHuman = $partition['total_human'] ?? '0 B';
|
|
@endphp
|
|
|
|
<style>
|
|
.server-charts-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
gap: 0;
|
|
}
|
|
@media (min-width: 640px) {
|
|
.server-charts-grid {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
}
|
|
@media (min-width: 1024px) {
|
|
.server-charts-grid {
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
}
|
|
}
|
|
.chart-card {
|
|
padding: 0.5rem;
|
|
}
|
|
.chart-container {
|
|
height: 280px;
|
|
width: 100%;
|
|
}
|
|
@media (min-width: 640px) {
|
|
.chart-container {
|
|
height: 320px;
|
|
}
|
|
}
|
|
@media (min-width: 1024px) {
|
|
.chart-container {
|
|
height: 360px;
|
|
}
|
|
}
|
|
.chart-label {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
margin-top: -4rem;
|
|
}
|
|
.chart-title {
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
color: rgb(17 24 39);
|
|
}
|
|
.dark .chart-title {
|
|
color: white;
|
|
}
|
|
.chart-subtitle {
|
|
font-size: 0.75rem;
|
|
color: rgb(107 114 128);
|
|
text-align: center;
|
|
margin-top: 0.25rem;
|
|
}
|
|
.dark .chart-subtitle {
|
|
color: rgb(156 163 175);
|
|
}
|
|
</style>
|
|
|
|
{{-- Main Charts Grid (3 columns) --}}
|
|
<div class="server-charts-grid">
|
|
{{-- CPU Usage Chart --}}
|
|
<div class="chart-card">
|
|
<div
|
|
x-data="{
|
|
chart: null,
|
|
value: {{ $cpuUsage }},
|
|
init() {
|
|
this.initChart();
|
|
Livewire.on('server-charts-updated', (data) => {
|
|
const newValue = data[0]?.cpu ?? data.cpu;
|
|
if (this.chart && newValue !== undefined) {
|
|
this.value = newValue;
|
|
const color = this.value >= 90 ? '#ef4444' : (this.value >= 70 ? '#f59e0b' : '#22c55e');
|
|
const shadowColor = this.value >= 90 ? 'rgba(239,68,68,0.45)' : (this.value >= 70 ? 'rgba(245,158,11,0.45)' : 'rgba(34,197,94,0.45)');
|
|
this.chart.setOption({
|
|
series: [{
|
|
itemStyle: { color: color, shadowColor: shadowColor },
|
|
data: [{ value: this.value }]
|
|
}]
|
|
});
|
|
}
|
|
});
|
|
},
|
|
initChart() {
|
|
if (typeof window.echarts === 'undefined') {
|
|
setTimeout(() => this.initChart(), 100);
|
|
return;
|
|
}
|
|
const isDark = document.documentElement.classList.contains('dark');
|
|
this.chart = echarts.init(this.$refs.chart);
|
|
const color = this.value >= 90 ? '#ef4444' : (this.value >= 70 ? '#f59e0b' : '#22c55e');
|
|
const shadowColor = this.value >= 90 ? 'rgba(239,68,68,0.45)' : (this.value >= 70 ? 'rgba(245,158,11,0.45)' : 'rgba(34,197,94,0.45)');
|
|
|
|
this.chart.setOption({
|
|
series: [{
|
|
type: 'gauge',
|
|
startAngle: 180,
|
|
endAngle: 0,
|
|
min: 0,
|
|
max: 100,
|
|
splitNumber: 10,
|
|
itemStyle: {
|
|
color: color,
|
|
shadowColor: shadowColor,
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 2,
|
|
shadowOffsetY: 2
|
|
},
|
|
progress: {
|
|
show: true,
|
|
roundCap: true,
|
|
width: 10
|
|
},
|
|
pointer: {
|
|
icon: 'path://M2090.36389,615.30999 L2090.36389,615.30999 C2091.48372,615.30999 2092.40383,616.194028 2092.44859,617.312956 L2096.90698,728.755929 C2097.05155,732.369577 2094.2393,735.416212 2090.62566,735.56078 C2090.53845,735.564269 2090.45117,735.566014 2090.36389,735.566014 L2090.36389,735.566014 C2086.74736,735.566014 2083.81557,732.63423 2083.81557,729.017692 C2083.81557,728.930412 2083.81732,728.84314 2083.82081,728.755929 L2088.2792,617.312956 C2088.32396,616.194028 2089.24407,615.30999 2090.36389,615.30999 Z',
|
|
length: '75%',
|
|
width: 16,
|
|
offsetCenter: [0, '5%']
|
|
},
|
|
axisLine: {
|
|
roundCap: true,
|
|
lineStyle: {
|
|
width: 10,
|
|
color: [[1, isDark ? '#374151' : '#e5e7eb']]
|
|
}
|
|
},
|
|
axisTick: {
|
|
splitNumber: 2,
|
|
lineStyle: {
|
|
width: 2,
|
|
color: isDark ? '#6b7280' : '#999'
|
|
}
|
|
},
|
|
splitLine: {
|
|
length: 12,
|
|
lineStyle: {
|
|
width: 3,
|
|
color: isDark ? '#6b7280' : '#999'
|
|
}
|
|
},
|
|
axisLabel: {
|
|
distance: 30,
|
|
color: isDark ? '#9ca3af' : '#999',
|
|
fontSize: 14
|
|
},
|
|
title: {
|
|
show: false
|
|
},
|
|
detail: {
|
|
backgroundColor: isDark ? '#1f2937' : '#fff',
|
|
borderColor: isDark ? '#4b5563' : '#999',
|
|
borderWidth: 2,
|
|
width: '40%',
|
|
lineHeight: 24,
|
|
height: 24,
|
|
borderRadius: 8,
|
|
offsetCenter: [0, '35%'],
|
|
valueAnimation: true,
|
|
formatter: function (value) {
|
|
return '{value|' + value.toFixed(0) + '}{unit|%}';
|
|
},
|
|
rich: {
|
|
value: {
|
|
fontSize: 20,
|
|
fontWeight: 'bolder',
|
|
color: isDark ? '#e5e7eb' : '#777'
|
|
},
|
|
unit: {
|
|
fontSize: 10,
|
|
color: isDark ? '#9ca3af' : '#999',
|
|
padding: [0, 0, -3, 2]
|
|
}
|
|
}
|
|
},
|
|
data: [{ value: this.value }]
|
|
}]
|
|
});
|
|
window.addEventListener('resize', () => this.chart?.resize());
|
|
},
|
|
destroy() {
|
|
this.chart?.dispose();
|
|
}
|
|
}"
|
|
wire:ignore
|
|
>
|
|
<div x-ref="chart" class="chart-container"></div>
|
|
</div>
|
|
<div class="chart-label">
|
|
<x-filament::icon icon="heroicon-o-cpu-chip" class="h-5 w-5 text-primary-500" />
|
|
<span class="chart-title">{{ __('CPU Usage') }}</span>
|
|
</div>
|
|
<div class="chart-subtitle">{{ $cpu['cores'] ?? 0 }} {{ __('cores') }} · {{ __('Load') }}: {{ $load['1min'] ?? 0 }} / {{ $load['5min'] ?? 0 }} / {{ $load['15min'] ?? 0 }}</div>
|
|
</div>
|
|
|
|
{{-- Memory Usage Chart --}}
|
|
<div class="chart-card">
|
|
<div
|
|
x-data="{
|
|
chart: null,
|
|
value: {{ $memUsage }},
|
|
init() {
|
|
this.initChart();
|
|
Livewire.on('server-charts-updated', (data) => {
|
|
const newValue = data[0]?.memory ?? data.memory;
|
|
if (this.chart && newValue !== undefined) {
|
|
this.value = newValue;
|
|
const color = this.value >= 90 ? '#ef4444' : (this.value >= 70 ? '#f59e0b' : '#22c55e');
|
|
const shadowColor = this.value >= 90 ? 'rgba(239,68,68,0.45)' : (this.value >= 70 ? 'rgba(245,158,11,0.45)' : 'rgba(34,197,94,0.45)');
|
|
this.chart.setOption({
|
|
series: [{
|
|
itemStyle: { color: color, shadowColor: shadowColor },
|
|
data: [{ value: this.value }]
|
|
}]
|
|
});
|
|
}
|
|
});
|
|
},
|
|
initChart() {
|
|
if (typeof window.echarts === 'undefined') {
|
|
setTimeout(() => this.initChart(), 100);
|
|
return;
|
|
}
|
|
const isDark = document.documentElement.classList.contains('dark');
|
|
this.chart = echarts.init(this.$refs.chart);
|
|
const color = this.value >= 90 ? '#ef4444' : (this.value >= 70 ? '#f59e0b' : '#22c55e');
|
|
const shadowColor = this.value >= 90 ? 'rgba(239,68,68,0.45)' : (this.value >= 70 ? 'rgba(245,158,11,0.45)' : 'rgba(34,197,94,0.45)');
|
|
|
|
this.chart.setOption({
|
|
series: [{
|
|
type: 'gauge',
|
|
startAngle: 180,
|
|
endAngle: 0,
|
|
min: 0,
|
|
max: 100,
|
|
splitNumber: 10,
|
|
itemStyle: {
|
|
color: color,
|
|
shadowColor: shadowColor,
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 2,
|
|
shadowOffsetY: 2
|
|
},
|
|
progress: {
|
|
show: true,
|
|
roundCap: true,
|
|
width: 10
|
|
},
|
|
pointer: {
|
|
icon: 'path://M2090.36389,615.30999 L2090.36389,615.30999 C2091.48372,615.30999 2092.40383,616.194028 2092.44859,617.312956 L2096.90698,728.755929 C2097.05155,732.369577 2094.2393,735.416212 2090.62566,735.56078 C2090.53845,735.564269 2090.45117,735.566014 2090.36389,735.566014 L2090.36389,735.566014 C2086.74736,735.566014 2083.81557,732.63423 2083.81557,729.017692 C2083.81557,728.930412 2083.81732,728.84314 2083.82081,728.755929 L2088.2792,617.312956 C2088.32396,616.194028 2089.24407,615.30999 2090.36389,615.30999 Z',
|
|
length: '75%',
|
|
width: 16,
|
|
offsetCenter: [0, '5%']
|
|
},
|
|
axisLine: {
|
|
roundCap: true,
|
|
lineStyle: {
|
|
width: 10,
|
|
color: [[1, isDark ? '#374151' : '#e5e7eb']]
|
|
}
|
|
},
|
|
axisTick: {
|
|
splitNumber: 2,
|
|
lineStyle: {
|
|
width: 2,
|
|
color: isDark ? '#6b7280' : '#999'
|
|
}
|
|
},
|
|
splitLine: {
|
|
length: 12,
|
|
lineStyle: {
|
|
width: 3,
|
|
color: isDark ? '#6b7280' : '#999'
|
|
}
|
|
},
|
|
axisLabel: {
|
|
distance: 30,
|
|
color: isDark ? '#9ca3af' : '#999',
|
|
fontSize: 14
|
|
},
|
|
title: {
|
|
show: false
|
|
},
|
|
detail: {
|
|
backgroundColor: isDark ? '#1f2937' : '#fff',
|
|
borderColor: isDark ? '#4b5563' : '#999',
|
|
borderWidth: 2,
|
|
width: '40%',
|
|
lineHeight: 24,
|
|
height: 24,
|
|
borderRadius: 8,
|
|
offsetCenter: [0, '35%'],
|
|
valueAnimation: true,
|
|
formatter: function (value) {
|
|
return '{value|' + value.toFixed(0) + '}{unit|%}';
|
|
},
|
|
rich: {
|
|
value: {
|
|
fontSize: 20,
|
|
fontWeight: 'bolder',
|
|
color: isDark ? '#e5e7eb' : '#777'
|
|
},
|
|
unit: {
|
|
fontSize: 10,
|
|
color: isDark ? '#9ca3af' : '#999',
|
|
padding: [0, 0, -3, 2]
|
|
}
|
|
}
|
|
},
|
|
data: [{ value: this.value }]
|
|
}]
|
|
});
|
|
window.addEventListener('resize', () => this.chart?.resize());
|
|
},
|
|
destroy() {
|
|
this.chart?.dispose();
|
|
}
|
|
}"
|
|
wire:ignore
|
|
>
|
|
<div x-ref="chart" class="chart-container"></div>
|
|
</div>
|
|
<div class="chart-label">
|
|
<x-filament::icon icon="heroicon-o-server" class="h-5 w-5 text-info-500" />
|
|
<span class="chart-title">{{ __('Memory') }}</span>
|
|
</div>
|
|
<div class="chart-subtitle">{{ number_format($memUsedGB, 1) }} GB {{ __('used') }} / {{ number_format($memTotalGB, 1) }} GB {{ __('total') }}</div>
|
|
</div>
|
|
|
|
{{-- Disk Usage Chart --}}
|
|
@if($partition)
|
|
<div class="chart-card">
|
|
<div
|
|
x-data="{
|
|
chart: null,
|
|
value: {{ $diskUsage }},
|
|
init() {
|
|
this.initChart();
|
|
Livewire.on('server-charts-updated', (data) => {
|
|
const newValue = data[0]?.disk ?? data.disk;
|
|
if (this.chart && newValue !== undefined) {
|
|
this.value = newValue;
|
|
const color = this.value >= 90 ? '#ef4444' : (this.value >= 70 ? '#f59e0b' : '#22c55e');
|
|
const shadowColor = this.value >= 90 ? 'rgba(239,68,68,0.45)' : (this.value >= 70 ? 'rgba(245,158,11,0.45)' : 'rgba(34,197,94,0.45)');
|
|
this.chart.setOption({
|
|
series: [{
|
|
itemStyle: { color: color, shadowColor: shadowColor },
|
|
data: [{ value: this.value }]
|
|
}]
|
|
});
|
|
}
|
|
});
|
|
},
|
|
initChart() {
|
|
if (typeof window.echarts === 'undefined') {
|
|
setTimeout(() => this.initChart(), 100);
|
|
return;
|
|
}
|
|
const isDark = document.documentElement.classList.contains('dark');
|
|
this.chart = echarts.init(this.$refs.chart);
|
|
const color = this.value >= 90 ? '#ef4444' : (this.value >= 70 ? '#f59e0b' : '#22c55e');
|
|
const shadowColor = this.value >= 90 ? 'rgba(239,68,68,0.45)' : (this.value >= 70 ? 'rgba(245,158,11,0.45)' : 'rgba(34,197,94,0.45)');
|
|
|
|
this.chart.setOption({
|
|
series: [{
|
|
type: 'gauge',
|
|
startAngle: 180,
|
|
endAngle: 0,
|
|
min: 0,
|
|
max: 100,
|
|
splitNumber: 10,
|
|
itemStyle: {
|
|
color: color,
|
|
shadowColor: shadowColor,
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 2,
|
|
shadowOffsetY: 2
|
|
},
|
|
progress: {
|
|
show: true,
|
|
roundCap: true,
|
|
width: 10
|
|
},
|
|
pointer: {
|
|
icon: 'path://M2090.36389,615.30999 L2090.36389,615.30999 C2091.48372,615.30999 2092.40383,616.194028 2092.44859,617.312956 L2096.90698,728.755929 C2097.05155,732.369577 2094.2393,735.416212 2090.62566,735.56078 C2090.53845,735.564269 2090.45117,735.566014 2090.36389,735.566014 L2090.36389,735.566014 C2086.74736,735.566014 2083.81557,732.63423 2083.81557,729.017692 C2083.81557,728.930412 2083.81732,728.84314 2083.82081,728.755929 L2088.2792,617.312956 C2088.32396,616.194028 2089.24407,615.30999 2090.36389,615.30999 Z',
|
|
length: '75%',
|
|
width: 16,
|
|
offsetCenter: [0, '5%']
|
|
},
|
|
axisLine: {
|
|
roundCap: true,
|
|
lineStyle: {
|
|
width: 10,
|
|
color: [[1, isDark ? '#374151' : '#e5e7eb']]
|
|
}
|
|
},
|
|
axisTick: {
|
|
splitNumber: 2,
|
|
lineStyle: {
|
|
width: 2,
|
|
color: isDark ? '#6b7280' : '#999'
|
|
}
|
|
},
|
|
splitLine: {
|
|
length: 12,
|
|
lineStyle: {
|
|
width: 3,
|
|
color: isDark ? '#6b7280' : '#999'
|
|
}
|
|
},
|
|
axisLabel: {
|
|
distance: 30,
|
|
color: isDark ? '#9ca3af' : '#999',
|
|
fontSize: 14
|
|
},
|
|
title: {
|
|
show: false
|
|
},
|
|
detail: {
|
|
backgroundColor: isDark ? '#1f2937' : '#fff',
|
|
borderColor: isDark ? '#4b5563' : '#999',
|
|
borderWidth: 2,
|
|
width: '40%',
|
|
lineHeight: 24,
|
|
height: 24,
|
|
borderRadius: 8,
|
|
offsetCenter: [0, '35%'],
|
|
valueAnimation: true,
|
|
formatter: function (value) {
|
|
return '{value|' + value.toFixed(0) + '}{unit|%}';
|
|
},
|
|
rich: {
|
|
value: {
|
|
fontSize: 20,
|
|
fontWeight: 'bolder',
|
|
color: isDark ? '#e5e7eb' : '#777'
|
|
},
|
|
unit: {
|
|
fontSize: 10,
|
|
color: isDark ? '#9ca3af' : '#999',
|
|
padding: [0, 0, -3, 2]
|
|
}
|
|
}
|
|
},
|
|
data: [{ value: this.value }]
|
|
}]
|
|
});
|
|
window.addEventListener('resize', () => this.chart?.resize());
|
|
},
|
|
destroy() {
|
|
this.chart?.dispose();
|
|
}
|
|
}"
|
|
wire:ignore
|
|
>
|
|
<div x-ref="chart" class="chart-container"></div>
|
|
</div>
|
|
<div class="chart-label">
|
|
<x-filament::icon icon="heroicon-o-circle-stack" class="h-5 w-5 text-warning-500" />
|
|
<span class="chart-title">{{ __('Disk') }} {{ $mountPoint }}</span>
|
|
</div>
|
|
<div class="chart-subtitle">{{ $usedHuman }} {{ __('used') }} / {{ $totalHuman }} {{ __('total') }}</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
</x-filament-widgets::widget>
|