From 7f91c4f9bfe1b51289502376db9bd0d60fb1af46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 27 Jul 2026 10:25:37 +0000 Subject: [PATCH] system-monitor: sortable columns in the pm2 view (click headers; asc/desc) Co-Authored-By: Claude Opus 4.8 --- .../src/apps/SystemMonitor/Pm2View.tsx | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx b/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx index d45cdb99..7192ca52 100644 --- a/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx +++ b/src/workspaces/officerdev/src/apps/SystemMonitor/Pm2View.tsx @@ -1,17 +1,31 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useClient } from 'hooks/useClient'; import { fmtBytes, fmtDuration } from './shared'; type Proc = { id: number; name: string; status: string; pid: number | null; cpuPct: number; memBytes: number; restarts: number; uptimeMs: number }; +type SortKey = 'id' | 'name' | 'status' | 'cpuPct' | 'memBytes' | 'restarts' | 'uptimeMs' | 'pid'; const POLL_MS = 2000; const statusColor = (s: string) => s === 'online' ? 'bg-emerald-500' : s === 'stopped' ? 'bg-muted-foreground/40' : s === 'errored' ? 'bg-red-500' : 'bg-amber-500'; +const COLS: { key: SortKey; label: string; align: 'left' | 'right' }[] = [ + { key: 'id', label: '#', align: 'right' }, + { key: 'name', label: 'Name', align: 'left' }, + { key: 'status', label: 'Status', align: 'left' }, + { key: 'cpuPct', label: 'CPU%', align: 'right' }, + { key: 'memBytes', label: 'Mem', align: 'right' }, + { key: 'restarts', label: '↺', align: 'right' }, + { key: 'uptimeMs', label: 'Uptime', align: 'left' }, + { key: 'pid', label: 'PID', align: 'right' }, +]; + export const Pm2View = () => { const { get } = useClient(); const [procs, setProcs] = useState(null); const [error, setError] = useState(null); + const [sortKey, setSortKey] = useState('cpuPct'); + const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc'); const alive = useRef(true); useEffect(() => { @@ -33,6 +47,25 @@ export const Pm2View = () => { }; }, []); + const toggleSort = (key: SortKey) => { + if (key === sortKey) setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')); + else { + setSortKey(key); + setSortDir(key === 'name' || key === 'status' ? 'asc' : 'desc'); // text asc, numeric desc by default + } + }; + + const sorted = useMemo(() => { + const arr = [...(procs ?? [])]; + arr.sort((a, b) => { + const av = a[sortKey] ?? 0; + const bv = b[sortKey] ?? 0; + const cmp = typeof av === 'string' && typeof bv === 'string' ? av.localeCompare(bv) : (av as number) - (bv as number); + return sortDir === 'asc' ? cmp : -cmp; + }); + return arr; + }, [procs, sortKey, sortDir]); + return (
@@ -42,18 +75,22 @@ export const Pm2View = () => { - - - - - - - - + {COLS.map((c) => ( + + ))} - {(procs ?? []).map((p) => ( + {sorted.map((p) => (
#NameStatusCPU%MemUptimePID + +
{p.id} {p.name}