Merge pull request #6657 from ShadowArcanist/shadow/metrics-visual-improvements

chore(ui): improve sentinel metrics
This commit is contained in:
Andras Bacsai
2025-09-22 09:46:26 +02:00
committed by GitHub
5 changed files with 320 additions and 223 deletions

View File

@@ -8,7 +8,7 @@ class Metrics extends Component
{ {
public $resource; public $resource;
public $chartId = 'container-cpu'; public $chartId = 'metrics';
public $data; public $data;

View File

@@ -6,10 +6,31 @@
@apply hidden!; @apply hidden!;
} }
@utility apexcharts-grid-borders {
@apply dark:hidden!;
}
@utility apexcharts-xaxistooltip { @utility apexcharts-xaxistooltip {
@apply hidden!; @apply hidden!;
} }
@utility apexcharts-tooltip-custom {
@apply bg-white dark:bg-coolgray-100 border border-neutral-200 dark:border-coolgray-300 rounded-lg shadow-lg p-3 text-sm;
min-width: 160px;
}
@utility apexcharts-tooltip-custom-value {
@apply text-neutral-700 dark:text-neutral-300 mb-1;
}
@utility apexcharts-tooltip-value-bold {
@apply font-bold text-black dark:text-white;
}
@utility apexcharts-tooltip-custom-title {
@apply text-xs text-neutral-500 dark:text-neutral-400 font-medium;
}
@utility input-sticky { @utility input-sticky {
@apply block py-1.5 w-full text-sm text-black rounded-sm border-0 ring-1 ring-inset dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 focus:ring-2 focus:ring-neutral-400 dark:focus:ring-coolgray-300; @apply block py-1.5 w-full text-sm text-black rounded-sm border-0 ring-1 ring-inset dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 focus:ring-2 focus:ring-neutral-400 dark:focus:ring-coolgray-300;
} }

View File

@@ -138,7 +138,8 @@
} }
} }
let theme = localStorage.theme let theme = localStorage.theme
let baseColor = '#FCD452' let cpuColor = '#1e90ff'
let ramColor = '#00ced1'
let textColor = '#ffffff' let textColor = '#ffffff'
let editorBackground = '#181818' let editorBackground = '#181818'
let editorTheme = 'blackboard' let editorTheme = 'blackboard'
@@ -149,12 +150,14 @@
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
} }
if (theme == 'dark') { if (theme == 'dark') {
baseColor = '#FCD452' cpuColor = '#1e90ff'
ramColor = '#00ced1'
textColor = '#ffffff' textColor = '#ffffff'
editorBackground = '#181818' editorBackground = '#181818'
editorTheme = 'blackboard' editorTheme = 'blackboard'
} else { } else {
baseColor = 'black' cpuColor = '#1e90ff'
ramColor = '#00ced1'
textColor = '#000000' textColor = '#000000'
editorBackground = '#ffffff' editorBackground = '#ffffff'
editorTheme = null editorTheme = null

View File

@@ -1,20 +1,19 @@
<div> <div>
<div class="flex items-center gap-2 "> <div class="flex items-center gap-2">
<h2>Metrics</h2> <h2>Metrics</h2>
</div> </div>
<div class="pb-4">Basic metrics for your container.</div> <div class="pb-4">Basic metrics for your application container.</div>
<div>
@if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose') @if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose')
<div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div> <div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div>
@elseif(!$resource->destination->server->isMetricsEnabled()) @elseif(!$resource->destination->server->isMetricsEnabled())
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div> <div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
<div> Go to <a class="underline dark:text-white" <div>Go to <a class="underline dark:text-white" href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to enable it.</div>
href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
enable
it.</div>
@else @else
@if (!str($resource->status)->contains('running')) @if (!str($resource->status)->contains('running'))
<div class="alert alert-warning">Metrics are only available when this resource is running!</div> <div class="alert alert-warning">Metrics are only available when the application container is running!</div>
@else @else
<div>
<x-forms.select label="Interval" wire:change="setInterval" id="interval"> <x-forms.select label="Interval" wire:change="setInterval" id="interval">
<option value="5">5 minutes (live)</option> <option value="5">5 minutes (live)</option>
<option value="10">10 minutes (live)</option> <option value="10">10 minutes (live)</option>
@@ -26,7 +25,7 @@
</x-forms.select> </x-forms.select>
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()" <div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()"
class="pt-5"> class="pt-5">
<h4>CPU (%)</h4> <h4>CPU Usage</h4>
<div wire:ignore id="{!! $chartId !!}-cpu"></div> <div wire:ignore id="{!! $chartId !!}-cpu"></div>
<script> <script>
@@ -34,6 +33,7 @@
const optionsServerCpu = { const optionsServerCpu = {
stroke: { stroke: {
curve: 'straight', curve: 'straight',
width: 2,
}, },
chart: { chart: {
height: '150px', height: '150px',
@@ -52,7 +52,7 @@
}, },
}, },
animations: { animations: {
enabled: false, enabled: true,
}, },
}, },
fill: { fill: {
@@ -72,7 +72,7 @@
show: true, show: true,
borderColor: '', borderColor: '',
}, },
colors: [baseColor], colors: [cpuColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
}, },
@@ -90,6 +90,21 @@
enabled: true, enabled: true,
marker: { marker: {
show: false, show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">CPU: <span class="apexcharts-tooltip-value-bold">' + value + '%</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
} }
}, },
legend: { legend: {
@@ -98,14 +113,13 @@
} }
const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`), optionsServerCpu); const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`), optionsServerCpu);
serverCpuChart.render(); serverCpuChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => { Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
checkTheme(); checkTheme();
serverCpuChart.updateOptions({ serverCpuChart.updateOptions({
series: [{ series: [{
data: chartData[0].seriesData, data: chartData[0].seriesData,
}], }],
colors: [baseColor], colors: [cpuColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
labels: { labels: {
@@ -121,6 +135,9 @@
show: true, show: true,
style: { style: {
colors: textColor, colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' %';
} }
} }
}, },
@@ -132,10 +149,9 @@
} }
}); });
}); });
});
</script> </script>
<h3>Memory (MB)</h3> <h4>Memory Usage</h4>
<div wire:ignore id="{!! $chartId !!}-memory"></div> <div wire:ignore id="{!! $chartId !!}-memory"></div>
<script> <script>
@@ -143,6 +159,7 @@
const optionsServerMemory = { const optionsServerMemory = {
stroke: { stroke: {
curve: 'straight', curve: 'straight',
width: 2,
}, },
chart: { chart: {
height: '150px', height: '150px',
@@ -161,7 +178,7 @@
}, },
}, },
animations: { animations: {
enabled: false, enabled: true,
}, },
}, },
fill: { fill: {
@@ -181,7 +198,7 @@
show: true, show: true,
borderColor: '', borderColor: '',
}, },
colors: [baseColor], colors: [ramColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
labels: { labels: {
@@ -205,6 +222,21 @@
enabled: true, enabled: true,
marker: { marker: {
show: false, show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">Memory: <span class="apexcharts-tooltip-value-bold">' + value + ' MB</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
} }
}, },
legend: { legend: {
@@ -214,14 +246,13 @@
const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`), const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`),
optionsServerMemory); optionsServerMemory);
serverMemoryChart.render(); serverMemoryChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => { Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
checkTheme(); checkTheme();
serverMemoryChart.updateOptions({ serverMemoryChart.updateOptions({
series: [{ series: [{
data: chartData[0].seriesData, data: chartData[0].seriesData,
}], }],
colors: [baseColor], colors: [ramColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
labels: { labels: {
@@ -238,6 +269,9 @@
show: true, show: true,
style: { style: {
colors: textColor, colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' MB';
} }
} }
}, },
@@ -249,9 +283,10 @@
} }
}); });
}); });
});
</script> </script>
</div> </div>
</div>
@endif @endif
@endif @endif
</div>
</div> </div>

View File

@@ -7,7 +7,7 @@
<x-server.sidebar :server="$server" activeMenu="metrics" /> <x-server.sidebar :server="$server" activeMenu="metrics" />
<div class="w-full"> <div class="w-full">
<h2>Metrics</h2> <h2>Metrics</h2>
<div class="pb-4">Basic metrics for your container.</div> <div class="pb-4">Basic metrics for your server.</div>
@if ($server->isMetricsEnabled()) @if ($server->isMetricsEnabled())
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()"> <div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
<x-forms.select label="Interval" wire:change="setInterval" id="interval"> <x-forms.select label="Interval" wire:change="setInterval" id="interval">
@@ -19,7 +19,7 @@
<option value="10080">1 week</option> <option value="10080">1 week</option>
<option value="43200">30 days</option> <option value="43200">30 days</option>
</x-forms.select> </x-forms.select>
<h4 class="pt-4">CPU (%)</h4> <h4 class="pt-4">CPU Usage</h4>
<div wire:ignore id="{!! $chartId !!}-cpu"></div> <div wire:ignore id="{!! $chartId !!}-cpu"></div>
<script> <script>
@@ -27,6 +27,7 @@
const optionsServerCpu = { const optionsServerCpu = {
stroke: { stroke: {
curve: 'straight', curve: 'straight',
width: 2,
}, },
chart: { chart: {
height: '150px', height: '150px',
@@ -45,7 +46,7 @@
}, },
}, },
animations: { animations: {
enabled: false, enabled: true,
}, },
}, },
fill: { fill: {
@@ -65,7 +66,7 @@
show: true, show: true,
borderColor: '', borderColor: '',
}, },
colors: [baseColor], colors: [cpuColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
}, },
@@ -83,6 +84,21 @@
enabled: true, enabled: true,
marker: { marker: {
show: false, show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">CPU: <span class="apexcharts-tooltip-value-bold">' + value + '%</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
} }
}, },
legend: { legend: {
@@ -99,7 +115,7 @@
series: [{ series: [{
data: chartData[0].seriesData, data: chartData[0].seriesData,
}], }],
colors: [baseColor], colors: [cpuColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
labels: { labels: {
@@ -115,6 +131,9 @@
show: true, show: true,
style: { style: {
colors: textColor, colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' %';
} }
} }
}, },
@@ -130,7 +149,7 @@
</script> </script>
<div> <div>
<h4>Memory (%)</h4> <h4>Memory Usage</h4>
<div wire:ignore id="{!! $chartId !!}-memory"></div> <div wire:ignore id="{!! $chartId !!}-memory"></div>
<script> <script>
@@ -138,6 +157,7 @@
const optionsServerMemory = { const optionsServerMemory = {
stroke: { stroke: {
curve: 'straight', curve: 'straight',
width: 2,
}, },
chart: { chart: {
height: '150px', height: '150px',
@@ -156,7 +176,7 @@
}, },
}, },
animations: { animations: {
enabled: false, enabled: true,
}, },
}, },
fill: { fill: {
@@ -176,7 +196,7 @@
show: true, show: true,
borderColor: '', borderColor: '',
}, },
colors: [baseColor], colors: [ramColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
labels: { labels: {
@@ -200,6 +220,21 @@
enabled: true, enabled: true,
marker: { marker: {
show: false, show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">Memory: <span class="apexcharts-tooltip-value-bold">' + value + '%</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
} }
}, },
legend: { legend: {
@@ -216,7 +251,7 @@
series: [{ series: [{
data: chartData[0].seriesData, data: chartData[0].seriesData,
}], }],
colors: [baseColor], colors: [ramColor],
xaxis: { xaxis: {
type: 'datetime', type: 'datetime',
labels: { labels: {
@@ -233,6 +268,9 @@
show: true, show: true,
style: { style: {
colors: textColor, colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' %';
} }
} }
}, },