feat(widgets): show WAN uptime in the UniFi site table

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
zacharyandClaude Opus 5 committed 2026-09-13 22:21:43 +02:00
1 parent e505e94644
commit d08bc942a2
2 files changed
+30 -6

No files matched your search

+2
View File
@@ -4273,6 +4273,7 @@
"column": {
"site": "Site",
"health": "Health (24h)",
"wanUptime": "WAN uptime",
"latency": "Latency",
"isp": "ISP",
"externalIp": "Public IP",
@@ -4292,6 +4293,7 @@
"down": "down",
"unknown": "not reported"
},
"wanUptime": "{value} %",
"latency": "{value} ms · {loss} % loss",
"latencyOnly": "{value} ms",
"devicesOnline": "{online} / {total}",
@@ -35,12 +35,13 @@ const compareUnknownLast = (first: unknown, second: unknown) => {
* Compare deux sites sur la colonne triée.
*
* `multiplier` ne s'applique qu'à la comparaison de deux valeurs CONNUES.
* `latencyMs` et `ispName` sont nullables, et `null` y signifie « pas mesuré »
* ou « pas rapporté » — jamais « zéro milliseconde » ni « chaîne vide ». Un
* inconnu n'est donc pas une valeur extrême et n'a sa place à aucun des deux
* bouts : il part en dernier DANS LES DEUX SENS, hors du multiplicateur. Le
* traiter comme une valeur ferait remonter en tête d'un tri croissant un site
* dont la latence n'a jamais été relevée, comme s'il était le plus rapide.
* `wanUptimePct`, `latencyMs` et `ispName` sont nullables, et `null` y signifie
* « pas mesuré » ou « pas rapporté » — jamais « zéro milliseconde », « 0 % » ni
* « chaîne vide ». Un inconnu n'est donc pas une valeur extrême et n'a sa place à
* aucun des deux bouts : il part en dernier DANS LES DEUX SENS, hors du
* multiplicateur. Le traiter comme une valeur ferait remonter en tête d'un tri
* croissant un site dont la latence n'a jamais été relevée, comme s'il était le
* plus rapide — et classerait une disponibilité inconnue parmi les pires.
*
* Les colonnes textuelles passent par `localeCompare` et non par `<` : sinon
* les noms accentués se classeraient en ordre de points de code.
@@ -51,6 +52,13 @@ const compareSites = (
accessor: DataTableSortStatus<UnifiSiteManagerSite>["columnAccessor"],
multiplier: number,
) => {
if (accessor === "wanUptimePct") {
const { wanUptimePct: firstUptime } = first;
const { wanUptimePct: secondUptime } = second;
if (firstUptime === null || secondUptime === null) return compareUnknownLast(firstUptime, secondUptime);
return (firstUptime - secondUptime) * multiplier;
}
if (accessor === "latencyMs") {
const { latencyMs: firstLatency } = first;
const { latencyMs: secondLatency } = second;
@@ -118,6 +126,20 @@ export default function UnifiSiteManagerWidget({
<HealthBar segments={site.health} summary={site.summary} formatHour={formatHour} labels={labels} />
),
},
{
accessor: "wanUptimePct",
// Intitulé SANS fenêtre temporelle, à dessein : le cloud ne documente pas
// la période que couvre `statistics.percentages.wanUptime`. Écrire
// « WAN uptime (24 h) » ou « (30 j) » affirmerait ce que nous ne savons
// pas. Ne pas « améliorer » cet en-tête en y ajoutant une durée.
title: t("widget.unifiSiteManager.column.wanUptime"),
sortable: true,
// `null` = le cloud n'a rien rapporté. Ni 0 %, ni 100 % : le tiret.
render: (site) =>
site.wanUptimePct === null
? EMPTY_VALUE
: t("widget.unifiSiteManager.wanUptime", { value: `${site.wanUptimePct}` }),
},
{
accessor: "latencyMs",
title: t("widget.unifiSiteManager.column.latency"),