Two more asks from mara's live review: - "i want the same filters for the cards tho, thats why i suggested separating data and filter from view" - extracted Table's filter *state* (not its popover UI, which stays table-shaped) into a new exported useTableFilters hook. Table calls it internally, unchanged behavior for every existing caller. AgentsPage now calls the same hook with the same storageKey, so card view and table view read/write one shared filter state instead of each having their own (or cards having none at all). Card view gets its own toolbar (AgentFilterBar) - a name search input plus one FilterMultiselect per multiselect column, same checkbox-list markup Table's own popover uses, driving the same state. Switching the view toggle no longer loses or hides whatever's filtered. - "why no separate panel? i mean a second panel on agent page" - replaced the modal Dialog with a real second Panel, always mounted (empty state when nothing's selected, so selecting an agent never shifts the page's own layout). Panel gained an optional `class` prop so the two panels can flex-size themselves in a row. List/detail panels sit side by side in a flex-wrap row that stacks on a narrow viewport - content-driven, same approach the shell's own nav uses, not a second hardcoded breakpoint. Selected card gets a highlight so it's clear which one the detail panel is showing. Verified with real CDP clicks: split layout with nothing selected, selecting a card highlights it and populates the detail panel, opening a card-view filter and checking a value narrows both the card list AND (after switching the toggle) the table to the identical row set.
64 lines
2.7 KiB
TypeScript
64 lines
2.7 KiB
TypeScript
// <Panel> — the one generic container primitive every route needs: a
|
|
// bordered surface with an optional title, no other opinions. Not a
|
|
// card-with-actions/footer/whatever kit — those get added the first
|
|
// time a real page actually needs one, not speculatively ahead of it.
|
|
//
|
|
// `actions` is that one addition: a slot in the title row, right-
|
|
// aligned, for a control that belongs next to the panel's own heading
|
|
// rather than its own row inside the body (design guide: "a control
|
|
// belongs next to the thing it affects, not tucked into a catch-all
|
|
// menu" — and per mara's review on the refresh-interval PR, not a
|
|
// dedicated row stealing vertical space from the panel's actual
|
|
// content either). `HivesPage`'s refresh-interval picker is the
|
|
// motivating caller.
|
|
//
|
|
// `icon` is a small header glyph, left of the title — the swarm-ui
|
|
// design guide's own whimsy reference (before this, the guide only
|
|
// pointed at the dashboard's matrix-rain background). Grew out of
|
|
// CreateAgentForm's one-off 🪪 dropped straight into a panel's body
|
|
// copy: mara's call on review was that a single ad-hoc emoji isn't
|
|
// whimsy in the guide's sense (small, delightful, *consistent*), it
|
|
// should be a real theme every panel can opt into the same way. Plain
|
|
// `string` (an emoji literal), not an icon-library asset — same
|
|
// lightweight-glyph precedent `RefreshIntervalPicker`'s 🕐 set before
|
|
// this. Chosen per panel, no default: not every panel needs one, and
|
|
// there's no single semantic mapping (e.g. "jobs" pages) worth
|
|
// hardcoding. `aria-hidden` — decorative only, the title text still
|
|
// carries the actual label, so this never becomes a second source of
|
|
// truth an assistive-tech user has to parse.
|
|
import type { ComponentChildren } from "preact";
|
|
import "./Panel.css";
|
|
|
|
export function Panel({
|
|
title,
|
|
icon,
|
|
actions,
|
|
children,
|
|
class: extraClass,
|
|
}: {
|
|
title?: string;
|
|
icon?: string;
|
|
actions?: ComponentChildren;
|
|
children: ComponentChildren;
|
|
/** Extra class on the outer `<section>` — e.g. flex-sizing a panel
|
|
* that's one of several sharing a row (`AgentsPage`'s list/detail
|
|
* split). Omit for the plain single-panel case every other caller is. */
|
|
class?: string;
|
|
}) {
|
|
return (
|
|
<section class={extraClass ? `ui-panel ${extraClass}` : "ui-panel"}>
|
|
{title || icon || actions ? (
|
|
<div class="ui-panel-header">
|
|
{icon ? (
|
|
<span class="ui-panel-icon" aria-hidden="true">
|
|
{icon}
|
|
</span>
|
|
) : null}
|
|
{title ? <h2 class="ui-panel-title">{title}</h2> : null}
|
|
{actions ? <div class="ui-panel-actions">{actions}</div> : null}
|
|
</div>
|
|
) : null}
|
|
<div class="ui-panel-body">{children}</div>
|
|
</section>
|
|
);
|
|
}
|