jobs: persist the panel layout across reloads
The page backed WorkspaceLayout with local useState, so onLayoutChange (fired on every resize) only updated ephemeral state — pane sizes reset to the default split on reload. Back it with useDashboardState under screens/jobs, like the other workspace routes, with a structural guard that falls back to the default when a persisted layout's panel ids no longer match PANEL_COMPONENTS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
import { WorkspaceLayout } from 'officerdev';
|
||||
import type { LayoutNode, PanelComponents } from 'officerdev';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { useDashboardState } from 'state/useDashboardState';
|
||||
import { Card } from '@/components/Card';
|
||||
import { ScriptJobDetail } from './ScriptJobDetail';
|
||||
import { DownloadJobDetail } from './DownloadJobDetail';
|
||||
@@ -312,12 +313,29 @@ const PANEL_COMPONENTS: PanelComponents = {
|
||||
'job-detail': JobDetailPanel,
|
||||
};
|
||||
|
||||
// One page for /jobs and /jobs/:id — master (list) + detail, resizable like /chat.
|
||||
// Guard the persisted layout against a stale shape (e.g. panel ids changed in a later build): the panels
|
||||
// render from PANEL_COMPONENTS by id, so a layout whose panel ids don't match ours would render blanks.
|
||||
// If it doesn't line up exactly, fall back to the default rather than trust the saved node.
|
||||
const collectPanelIds = (node: LayoutNode, acc: Set<string>): Set<string> => {
|
||||
if (node.type === 'panel') acc.add(node.id);
|
||||
else for (const child of node.children) collectPanelIds(child.node, acc);
|
||||
return acc;
|
||||
};
|
||||
|
||||
const matchesPanelSet = (node: LayoutNode): boolean => {
|
||||
const ids = collectPanelIds(node, new Set<string>());
|
||||
const expected = Object.keys(PANEL_COMPONENTS);
|
||||
return ids.size === expected.length && expected.every((id) => ids.has(id));
|
||||
};
|
||||
|
||||
// One page for /jobs and /jobs/:id — master (list) + detail, resizable like /chat. The layout is
|
||||
// persisted per-user via useDashboardState (screens/ namespace), so pane sizes survive reloads.
|
||||
export const JobsPage = () => {
|
||||
const [layout, setLayout] = useState<LayoutNode>(JOBS_LAYOUT);
|
||||
const { value, setValue } = useDashboardState<LayoutNode>('screens/jobs', JOBS_LAYOUT);
|
||||
const layout = matchesPanelSet(value) ? value : JOBS_LAYOUT;
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<WorkspaceLayout layout={layout} onLayoutChange={setLayout} components={PANEL_COMPONENTS} noHeader />
|
||||
<WorkspaceLayout layout={layout} onLayoutChange={setValue} components={PANEL_COMPONENTS} noHeader />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user