From e3a0279c0038ef5452c72270e8473391d799c1e2 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Wed, 19 Aug 2026 11:09:02 +0200
Subject: [PATCH] fix(ui): flag pending proxy updates and Sentinel outages
---
.../views/components/server/sidebar.blade.php | 1 +
.../components/server/status-summary.blade.php | 17 ++++++++++++-----
resources/views/livewire/dashboard.blade.php | 2 +-
resources/views/livewire/server/index.blade.php | 3 ++-
tests/Feature/ServerSidebarIconsTest.php | 7 +++++++
.../Feature/ServerStatusIndicatorDesignTest.php | 14 ++++++++++++--
6 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php
index 41cbd7ce9d..46e433e8a5 100644
--- a/resources/views/components/server/sidebar.blade.php
+++ b/resources/views/components/server/sidebar.blade.php
@@ -70,6 +70,7 @@
'icon' => 'shield-star',
'group' => 'Platform',
'visible' => $server->isFunctional() && ! $server->isSwarm() && ! $server->settings->is_build_server && auth()->user()?->can('viewSentinel', $server),
+ 'warning' => $server->isSentinelEnabled() && ! $server->isSentinelLive(),
'children' => [
['label' => 'Configuration', 'route' => 'server.sentinel', 'active' => request()->routeIs('server.sentinel'), 'icon' => 'settings'],
['label' => 'Logs', 'route' => 'server.sentinel.logs', 'active' => request()->routeIs('server.sentinel.logs'), 'icon' => 'file-content'],
diff --git a/resources/views/components/server/status-summary.blade.php b/resources/views/components/server/status-summary.blade.php
index 7a5522b541..5e732fade8 100644
--- a/resources/views/components/server/status-summary.blade.php
+++ b/resources/views/components/server/status-summary.blade.php
@@ -6,7 +6,10 @@
@php
$serverReady = $server->isFunctional();
- $proxyNeedsAttention = $server->proxySet() && ! in_array($proxyStatus, ['running'], true);
+ $proxyUpdateAvailable = $server->proxySet()
+ && ($server->hasCurrentTraefikOutdatedInfo() || $server->hasPendingProxyConfiguration());
+ $proxyNeedsAttention = $server->proxySet()
+ && (! in_array($proxyStatus, ['running'], true) || $proxyUpdateAvailable);
$sentinelNeedsAttention = $showSentinelStatus && ! $server->isSentinelLive();
[$summaryLabel, $summaryType] = match (true) {
@@ -58,9 +61,9 @@
class="listbox-option gap-2.5!" @click="open = false" role="menuitem">
$proxyStatus === 'running',
- 'bg-warning' => in_array($proxyStatus, ['starting', 'restarting', 'stopping'], true),
- 'bg-error' => ! in_array($proxyStatus, ['running', 'starting', 'restarting', 'stopping'], true),
+ 'bg-success' => $proxyStatus === 'running' && ! $proxyUpdateAvailable,
+ 'bg-warning' => $proxyNeedsAttention && ($proxyUpdateAvailable || in_array($proxyStatus, ['starting', 'restarting', 'stopping'], true)),
+ 'bg-error' => $proxyNeedsAttention && ! $proxyUpdateAvailable && ! in_array($proxyStatus, ['starting', 'restarting', 'stopping'], true),
])>
Proxy
{{ str($proxyStatus ?: 'unknown')->headline() }}
@@ -69,7 +72,11 @@
@if ($showSentinelStatus)
-
+ ! $sentinelNeedsAttention,
+ 'bg-warning' => $sentinelNeedsAttention,
+ ])>
Sentinel
{{ $server->isSentinelLive() ? 'In sync' : 'Out of sync' }}
diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php
index 175fdb39a7..a8f4d0369d 100644
--- a/resources/views/livewire/dashboard.blade.php
+++ b/resources/views/livewire/dashboard.blade.php
@@ -175,7 +175,7 @@
@foreach ($dashboardServers as $server)
@php
- $proxyNeedsAttention = $server->proxySet() && $server->proxy->status !== 'running';
+ $proxyNeedsAttention = $server->proxySet() && ($server->proxy->status !== 'running' || $server->hasCurrentTraefikOutdatedInfo());
$sentinelNeedsAttention = $server->isSentinelEnabled() && ! $server->isSentinelLive();
[$serverStatus, $serverStatusType] = match (true) {
diff --git a/resources/views/livewire/server/index.blade.php b/resources/views/livewire/server/index.blade.php
index d89e8b891f..112e052094 100644
--- a/resources/views/livewire/server/index.blade.php
+++ b/resources/views/livewire/server/index.blade.php
@@ -33,7 +33,8 @@
&& $server->settings->is_usable
&& ! $server->settings->force_disabled
&& ! $isTransferredAway;
- $proxyNeedsAttention = $isReady && $server->proxySet() && $server->proxy->status !== 'running';
+ $proxyNeedsAttention = $isReady && $server->proxySet()
+ && ($server->proxy->status !== 'running' || $server->hasCurrentTraefikOutdatedInfo());
$sentinelNeedsAttention = $isReady && $server->isSentinelEnabled() && ! $server->isSentinelLive();
$status = match (true) {
diff --git a/tests/Feature/ServerSidebarIconsTest.php b/tests/Feature/ServerSidebarIconsTest.php
index 205429d678..12d33b6e38 100644
--- a/tests/Feature/ServerSidebarIconsTest.php
+++ b/tests/Feature/ServerSidebarIconsTest.php
@@ -36,6 +36,13 @@ it('uses the shield-star reicon for sentinel in the server sidebar', function ()
->toMatch("/'label' => 'Sentinel',\s*'route' => 'server\.sentinel',\s*'active' => request\(\)->routeIs\('server\.sentinel', 'server\.sentinel\.\*'\),\s*'icon' => 'shield-star'/s");
});
+it('shows a warning icon when sentinel is enabled but not working', function () {
+ $contents = file_get_contents(resource_path('views/components/server/sidebar.blade.php'));
+
+ expect($contents)
+ ->toContain("'warning' => \$server->isSentinelEnabled() && ! \$server->isSentinelLive()");
+});
+
it('uses the network reicon for proxy in the server sidebar', function () {
$contents = file_get_contents(resource_path('views/components/server/sidebar.blade.php'));
diff --git a/tests/Feature/ServerStatusIndicatorDesignTest.php b/tests/Feature/ServerStatusIndicatorDesignTest.php
index 090d6d816a..5e2b8b5faa 100644
--- a/tests/Feature/ServerStatusIndicatorDesignTest.php
+++ b/tests/Feature/ServerStatusIndicatorDesignTest.php
@@ -13,7 +13,7 @@ test('server cards use warning icons instead of colored icon borders', function
expect(substr_count($serverIndex, 'toBe(1)
->and($serverIndex)
- ->toContain("\$proxyNeedsAttention = \$isReady && \$server->proxySet() && \$server->proxy->status !== 'running'")
+ ->toContain("&& (\$server->proxy->status !== 'running' || \$server->hasCurrentTraefikOutdatedInfo())")
->toContain('$sentinelNeedsAttention = $isReady && $server->isSentinelEnabled() && ! $server->isSentinelLive()')
->toContain("\$proxyNeedsAttention || \$sentinelNeedsAttention => 'warning'")
->toContain("\$isReady => 'success'")
@@ -29,11 +29,21 @@ test('dashboard server cards warn when proxy or sentinel needs attention', funct
$dashboard = file_get_contents(resource_path('views/livewire/dashboard.blade.php'));
expect($dashboard)
- ->toContain("\$proxyNeedsAttention = \$server->proxySet() && \$server->proxy->status !== 'running'")
+ ->toContain("\$proxyNeedsAttention = \$server->proxySet() && (\$server->proxy->status !== 'running' || \$server->hasCurrentTraefikOutdatedInfo())")
->toContain('$sentinelNeedsAttention = $server->isSentinelEnabled() && ! $server->isSentinelLive()')
->toContain("\$proxyNeedsAttention || \$sentinelNeedsAttention => ['Attention required', 'warning']");
});
+test('server status summary uses warning indicators for proxy updates and sentinel outages', function () {
+ $summary = file_get_contents(resource_path('views/components/server/status-summary.blade.php'));
+
+ expect($summary)
+ ->toContain('$server->hasCurrentTraefikOutdatedInfo()')
+ ->toContain("'bg-warning' => \$proxyNeedsAttention && (\$proxyUpdateAvailable")
+ ->toContain("'bg-warning' => \$sentinelNeedsAttention")
+ ->not->toContain("\$server->isSentinelLive() ? 'bg-success' : 'bg-error'");
+});
+
test('server table keeps status text without a badge', function () {
$serverIndex = file_get_contents(resource_path('views/livewire/server/index.blade.php'));