<#880 Redesigned view for carves and queries> Pull...
# osctrl
g
#880 Redesigned view for carves and queries Pull request opened by javuto Summary Reworks the carve detail and query detail views in the SPA to surface more of the data the backend already returns and to give operators explicit refresh control. Frontend-only; no API or storage changes. Carve detail (
CarveDetailPage
)
• Summary band above the carved-files table: Files (count + archived count), Total size (sum of
carve_size
), Blocks (completed/total + %), and Max block size (
max(f.block_size)
across the carve's files). An operator can read the carve's state at a glance instead of scanning rows. • Expanded files table — added Path and Archived columns. The redundant per-row
Block size
column was removed (block size is a per-carve value); it now lives once as Max block size in the summary. The table is wrapped in an
overflow-x-auto
container with a
min-w
so it fills the available width and scrolls only when genuinely narrow. Columns: Node · Path · Status · Size · Blocks · Completed · Archived · Download. • Expires added to the carve header (was missing entirely) — shows the relative expiration or
never
for a no-expiration carve. • Refresh button added to the "Carved files" header, mirroring the query detail page. It invalidates the carve, the carves list, and the node lookup so an operator watching blocks land can pull the latest state now instead of waiting for the 15s poll. Disabled while a fetch is in flight, with a "refreshing…" indicator. Query detail (
QueryDetailPage
)
• Refresh now refreshes everything. The Refresh button previously invalidated only the query and its results; it now calls
qc.invalidateQueries()
with no key, refetching every active query on the page (query, results, queries list, and any other live data) so an operator watching a query land gets a fully fresh view. • Expiration is always shown. The detail previously rendered "Expires" only when a real expiration existed, so a no-expiration query showed nothing — an operator couldn't tell unset from hidden. It now always renders Expires, showing
never
for the zero/`0001` case (timestamp in the tooltip). The set side was already correct (run page sends `exp_hours`; backend maps
0 → time.Time{}
and otherwise
now + hours
), so only the display changed. Behavior / risk notes • Pure view-layer changes; no backend, routing, auth, or persistence changes. • The carve files table is wider (8 columns + download) and intentionally scrolls horizontally on narrow viewports rather than collapsing columns or truncating data. •
Max block size
is derived client-side from the existing
CarveFile.block_size
field — no new API field. Validation •
tsc --noEmit
clean. • New
CarveDetailPage.test.tsx
cases: the Refresh button reloads the carve; a 2-file carve renders the summary band (Files / Total size / Blocks / Max block size), the
Expires: never
header, and the new Path / Archived columns. Passes. • New
QueryDetailPage.test.tsx
cases: the Refresh button reloads both the query and its results (refresh everything); a no-expiration query shows
Expires: never
. Passes. • Existing carve/query detail tests still pass (8). Full vitest: 126 passed; the only failures are the pre-existing `LoginPage`/`ProfilePage` ones (jsdom
localStorage
), unchanged by this PR. Files •
frontend/src/features/carves/CarveDetailPage.tsx
— summary band, expanded table, Expires header, refresh button. •
frontend/src/features/carves/CarveDetailPage.test.tsx
— summary/columns/expiration + refresh tests. •
frontend/src/features/queries/QueryDetailPage.tsx
— refresh-everything, always-visible expiration. •
frontend/src/features/queries/QueryDetailPage.test.tsx
— refresh + expiration tests. jmpsec/osctrl