From ed1e3c1f043833499221138b0cd7bfc7afd1d4c4 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ahmed Date: Thu, 22 Oct 2026 16:54:05 +0300 Subject: [PATCH 1/2] app: label each cluster column on Licenses --- apps/app/src/pages/Licenses.tsx | 42 ++++++++++++++++++----------- apps/app/src/styles.css | 4 +++ apps/app/src/test/licenses.test.tsx | 8 +++--- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/apps/app/src/pages/Licenses.tsx b/apps/app/src/pages/Licenses.tsx index 78d2276..3d99d09 100644 --- a/apps/app/src/pages/Licenses.tsx +++ b/apps/app/src/pages/Licenses.tsx @@ -18,7 +18,7 @@ function LastSeen({unix, skewMs}: {unix: number; skewMs: number}) { const id = setInterval(() => setNow(Date.now()), 60_000); return () => clearInterval(id); }, []); - return Last seen {fmtLastSeenAgo(unix, now + skewMs)}; + return {fmtLastSeenAgo(unix, now + skewMs)}; } function statusOf(l: License): {label: string; tone: 'ok' | 'warn' | 'bad'} { @@ -49,23 +49,33 @@ function ClusterHalf({license, cs}: {license: License; cs: ClusterState}) { body =
; } else if (c) { body = ( -
-
- {c.id_short} +
+
+
Cluster
+
{c.id_short}
-
- +
+
Last seen
+
+ +
-
-
{c.version ? `v${c.version.replace(/^v/, '')}` : ''}
- {cs.latest && isOutdated(c.version, cs.latest) && ( - - Update available: v{cs.latest.replace(/^v/, '')} - - )} +
+
Version
+
+ {c.version ? `v${c.version.replace(/^v/, '')}` : ''} + {cs.latest && isOutdated(c.version, cs.latest) && ( + + Update available: v{cs.latest.replace(/^v/, '')} + + )} +
-
{c.nodes != null ? plural(c.nodes, 'node', 'nodes') : ''}
-
+
+
Nodes
+
{c.nodes != null ? plural(c.nodes, 'node', 'nodes') : ''}
+
+
); } else if (license.status === 'active') { body = ( @@ -80,7 +90,7 @@ function ClusterHalf({license, cs}: {license: License; cs: ClusterState}) { if (!body && !ended) return null; return (
-

Cluster

+ {!c &&

Cluster

} {ended} {body}
diff --git a/apps/app/src/styles.css b/apps/app/src/styles.css index b129e68..f03045c 100644 --- a/apps/app/src/styles.css +++ b/apps/app/src/styles.css @@ -227,3 +227,7 @@ form .muted.small { margin-top: 14px; font-size: 13px; } .key-row .key { margin: 0; overflow-x: auto; } .license-main p { margin: 0; } .license-name { margin-bottom: 4px; } +.cluster-grid { margin: 0; } +.cluster-grid dt { margin: 0 0 4px; } +.cluster-grid dd { margin: 0; font-family: var(--font-mono); font-size: 13px; } +.cluster-grid dd, .cluster-grid dd .cluster-version { color: var(--text-muted); font-weight: 400; font-size: 13px; } diff --git a/apps/app/src/test/licenses.test.tsx b/apps/app/src/test/licenses.test.tsx index eeccf68..67ed5e2 100644 --- a/apps/app/src/test/licenses.test.tsx +++ b/apps/app/src/test/licenses.test.tsx @@ -225,7 +225,7 @@ describe('/licenses', () => { expect(await screen.findByText('7c1e04ab')).toHaveClass('mono'); expect(screen.getByText('v1.0.0')).toBeInTheDocument(); expect(screen.getByText('3 nodes')).toBeInTheDocument(); - expect(screen.getAllByText(/^Last seen /).length).toBe(2); + expect(screen.getAllByText(/ ago$|^just now$/).length).toBe(2); expect(screen.queryByText(/Inactive|Stopped|Waiting|check-in/i)).toBeNull(); expect(screen.queryByText(/reach kwatch.dev/)).toBeNull(); }); @@ -236,10 +236,10 @@ describe('/licenses', () => { const t = Math.floor(Date.now() / 1000); withClusters([cl({last_seen: t - 20 * 60})]); renderAt('/licenses'); - const el = await screen.findByText('Last seen 20 min ago'); + const el = await screen.findByText('20 min ago'); expect(el).toHaveAttribute('title', expect.stringMatching(/^\d{4}-\d\d-\d\d \d\d:\d\d UTC$/)); await act(async () => { await vi.advanceTimersByTimeAsync(60_000); }); - expect(screen.getByText('Last seen 21 min ago')).toBeInTheDocument(); + expect(screen.getByText('21 min ago')).toBeInTheDocument(); } finally { vi.useRealTimers(); } @@ -260,7 +260,7 @@ describe('/licenses', () => { 'GET /account/clusters': {body: {clusters: [cl({last_seen: t - 600})], srv_time: t + 3600}}, }); renderAt('/licenses'); - expect(await screen.findByText('Last seen 1 h ago')).toBeInTheDocument(); + expect(await screen.findByText('1 h ago')).toBeInTheDocument(); }); it('sorts non-active licenses first, then newest first', async () => { From 52cbf98e29a6438349204767d47695d2354d8947 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ahmed Date: Thu, 22 Oct 2026 16:56:28 +0300 Subject: [PATCH 2/2] app: rename Last seen to Last update --- apps/app/src/pages/Licenses.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/app/src/pages/Licenses.tsx b/apps/app/src/pages/Licenses.tsx index 3d99d09..b2163a3 100644 --- a/apps/app/src/pages/Licenses.tsx +++ b/apps/app/src/pages/Licenses.tsx @@ -55,7 +55,7 @@ function ClusterHalf({license, cs}: {license: License; cs: ClusterState}) {
{c.id_short}
-
Last seen
+
Last update