feat(dashboard): refresh metrics and color server status indicators

Refresh visible dashboard metrics every minute and after backgrounding. Replace server status badges with accessible color-coded icon borders and status text.
This commit is contained in:
Andras Bacsai committed 2026-08-10 14:04:24 +02:00
1 parent 3f47524baf
commit 7037633f45
6 files changed
+103 -28

No files matched your search

+7 -8
View File
@@ -187,8 +187,13 @@
@endif
<div class="pointer-events-none relative z-10 flex min-w-0 items-start gap-3">
<div
class="flex size-8 shrink-0 items-center justify-center rounded-lg border border-neutral-200 bg-neutral-50 text-neutral-500 dark:border-white/[0.08] dark:bg-white/[0.04] dark:text-fg-dim">
<div title="{{ $serverStatus }}" aria-label="Server status: {{ $serverStatus }}"
@class([
'flex size-8 shrink-0 items-center justify-center rounded-lg border bg-neutral-50 text-neutral-500 dark:bg-white/[0.04] dark:text-fg-dim',
'border-emerald-500/70' => $serverStatusType === 'success',
'border-amber-500/70' => $serverStatusType === 'warning',
'border-red-500/70' => $serverStatusType === 'error',
])>
<x-reicon name="servers" class="size-4" />
</div>
<div class="min-w-0 flex-1">
@@ -201,12 +206,6 @@
</p>
</div>
</div>
@unless ($server->isMetricsEnabled())
<div class="pointer-events-none relative z-10 mt-auto flex items-center pt-4">
<x-status-badge :status="$serverStatus" :type="$serverStatusType" />
</div>
@endunless
</a>
@endforeach
</div>
@@ -1,6 +1,37 @@
<div
class="absolute right-0 bottom-0 h-2/3 w-full rounded-b-xl [&_.apexcharts-svg]:overflow-hidden [&_.apexcharts-svg]:rounded-b-xl [&_.apexcharts-tooltip]:z-30!"
x-init="$wire.loadData()">
x-data="{
hiddenAt: null,
refreshInterval: null,
visibilityHandler: null,
init() {
this.visibilityHandler = () => {
if (document.hidden) {
this.hiddenAt = Date.now();
return;
}
if (this.hiddenAt && Date.now() - this.hiddenAt >= 60000) {
$wire.loadData();
}
this.hiddenAt = null;
};
document.addEventListener('visibilitychange', this.visibilityHandler);
this.refreshInterval = window.setInterval(() => {
if (!document.hidden) {
$wire.loadData();
}
}, 60000);
$wire.loadData();
},
destroy() {
window.clearInterval(this.refreshInterval);
document.removeEventListener('visibilitychange', this.visibilityHandler);
},
}">
<div wire:ignore id="dashboard-server-metrics-{{ $server->uuid }}" class="h-full w-full"></div>
@script
+15 -19
View File
@@ -41,14 +41,19 @@
default => 'Validation required',
};
$statusType = match (true) {
$isReady => 'success',
$isTransferredAway || $server->settings->force_disabled => 'error',
default => 'warning',
};
return [
'uuid' => $server->uuid,
'name' => $server->name,
'description' => $server->description ?: 'No description',
'href' => route('server.show', ['server_uuid' => $server->uuid]),
'status' => $status,
'statusType' => $isReady ? 'success' : 'error',
'ready' => $isReady,
'statusType' => $statusType,
];
})->values();
@endphp
@@ -121,8 +126,9 @@
<a :href="server.href" {{ wireNavigate() }}
class="group relative flex min-h-28 flex-col rounded-xl border border-neutral-200 bg-white p-3 shadow-sm transition-all hover:-translate-y-px hover:border-neutral-300 hover:no-underline hover:shadow-md dark:border-white/[0.08] dark:bg-white/[0.025] dark:hover:border-white/[0.14]">
<div class="flex items-start gap-3">
<div
class="flex size-8 shrink-0 items-center justify-center rounded-lg border border-neutral-200 bg-neutral-50 text-neutral-500 dark:border-white/[0.08] dark:bg-white/[0.04] dark:text-fg-dim">
<div :title="server.status" :aria-label="`Server status: ${server.status}`"
class="flex size-8 shrink-0 items-center justify-center rounded-lg border bg-neutral-50 text-neutral-500 dark:bg-white/[0.04] dark:text-fg-dim"
:class="server.statusType === 'success' ? 'border-emerald-500/70' : server.statusType === 'warning' ? 'border-amber-500/70' : 'border-red-500/70'">
<x-reicon name="servers" class="size-4" />
</div>
<div class="min-w-0 flex-1">
@@ -132,13 +138,6 @@
x-text="server.description"></p>
</div>
</div>
<div class="mt-auto flex items-center pt-4">
<x-status-badge dynamic>
<span class="size-1.5 rounded-full"
:class="server.ready ? 'bg-emerald-500' : 'bg-red-500'"></span>
<span x-text="server.status"></span>
</x-status-badge>
</div>
</a>
</template>
</div>
@@ -154,8 +153,9 @@
<a :href="server.href" {{ wireNavigate() }}
class="grid min-h-14 min-w-[480px] grid-cols-[minmax(0,1fr)_9.5rem] items-center border-b border-neutral-200 px-4 py-2.5 text-[12px] transition-colors last:border-b-0 hover:bg-neutral-50 hover:no-underline dark:border-white/[0.07] dark:hover:bg-white/[0.025]">
<div class="flex min-w-0 items-center gap-3">
<div
class="flex size-8 shrink-0 items-center justify-center rounded-lg border border-neutral-200 bg-neutral-50 text-neutral-500 dark:border-white/[0.08] dark:bg-white/[0.035] dark:text-fg-dim">
<div :title="server.status" :aria-label="`Server status: ${server.status}`"
class="flex size-8 shrink-0 items-center justify-center rounded-lg border bg-neutral-50 text-neutral-500 dark:bg-white/[0.035] dark:text-fg-dim"
:class="server.statusType === 'success' ? 'border-emerald-500/70' : server.statusType === 'warning' ? 'border-amber-500/70' : 'border-red-500/70'">
<x-reicon name="servers" class="size-4" />
</div>
<div class="min-w-0">
@@ -165,12 +165,8 @@
x-text="server.description"></p>
</div>
</div>
<div>
<x-status-badge dynamic>
<span class="size-1.5 rounded-full"
:class="server.ready ? 'bg-emerald-500' : 'bg-red-500'"></span>
<span x-text="server.status"></span>
</x-status-badge>
<div class="text-[11px] font-medium text-neutral-600 dark:text-fg-dim">
<span x-text="server.status"></span>
</div>
</a>
</template>