From d08bc942a2e5d64a7660ebab3d15ba333b937ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zachary=20Gu=C3=A9not?= Date: Sun, 13 Sep 2026 15:52:05 +0200 Subject: [PATCH] feat(widgets): show WAN uptime in the UniFi site table Co-Authored-By: Claude Opus 5 --- packages/translation/src/lang/en.json | 2 ++ .../src/unifi-site-manager/component.tsx | 34 +++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 60ad00585..5a81c0e8c 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -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}", diff --git a/packages/widgets/src/unifi-site-manager/component.tsx b/packages/widgets/src/unifi-site-manager/component.tsx index 04094e955..4acf2e41c 100644 --- a/packages/widgets/src/unifi-site-manager/component.tsx +++ b/packages/widgets/src/unifi-site-manager/component.tsx @@ -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["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({ ), }, + { + 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"),