redesign automation page with simple list + detail panels

Replace 8-category layout with flat task list and detail view using
WorkspaceView. Searchable list with mode badges, run/delete actions,
create dialog. Remove 13 unused component files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 07:24:57 +00:00
co-authored by Claude Opus 4.6
parent 24ac7ac796
commit 0f1e1f35f1
17 changed files with 460 additions and 1640 deletions
@@ -1,100 +1,41 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { useIsMobile } from 'hooks/useIsMobile';
import { usePanelChannel } from 'hooks/usePanelChannel';
import { useCallback, useMemo } from 'react';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceView } from 'officerdev';
import { useIsMobile } from 'hooks/useIsMobile';
import { usePanelChannel } from 'hooks/usePanelChannel';
import { useDashboardState } from 'state/useDashboardState';
import type { TaskSummary } from 'officerdev';
import { AutomationRightPanel } from './AutomationRightPanel';
import type { AutomationSelection } from './AutomationRightPanel';
import { AutomationEditChat } from './AutomationEditChat';
import { AutomationSidebar } from './AutomationSidebar';
import { AutomationList } from './AutomationList';
import { AutomationDetail } from './AutomationDetail';
const CHAT_PANEL_ID = 'automation-chat';
export type { TaskSummary as SelectedTask };
const defaultLayout: LayoutNode = {
type: 'group',
id: 'automation-root',
direction: 'horizontal',
children: [
{ node: { type: 'panel', id: 'automation-left', appType: null }, size: 20 },
{ node: { type: 'panel', id: 'automation-right', appType: null }, size: 80 },
{ node: { type: 'panel', id: 'automation-list', appType: null }, size: 30 },
{ node: { type: 'panel', id: 'automation-detail', appType: null }, size: 70 },
],
};
const hasChatPanel = (layout: LayoutNode): boolean => {
if (layout.type === 'panel') return layout.id === CHAT_PANEL_ID;
return layout.children.some((c) => hasChatPanel(c.node));
};
const addChatPanel = (layout: LayoutNode): LayoutNode => {
if (layout.type !== 'group') return layout;
const clone = structuredClone(layout);
const rightChild = clone.children[1];
if (!rightChild) return clone;
// Wrap the right panel in a vertical group with the chat panel
const rightSize = rightChild.size;
rightChild.size = rightSize;
clone.children[1] = {
node: {
type: 'group',
id: 'automation-right-group',
direction: 'vertical',
children: [
{ node: rightChild.node, size: 50 },
{ node: { type: 'panel', id: CHAT_PANEL_ID, appType: null }, size: 50 },
],
},
size: rightSize,
};
return clone;
};
const removeChatPanel = (layout: LayoutNode): LayoutNode => {
if (layout.type !== 'group') return layout;
const clone = structuredClone(layout);
const rightChild = clone.children[1];
if (!rightChild || rightChild.node.type !== 'group') return clone;
// Unwrap: pull the right panel out of the vertical group
const rightGroup = rightChild.node;
const mainPanel = rightGroup.children.find((c) => c.node.type === 'panel' && (c.node as { id: string }).id !== CHAT_PANEL_ID);
if (mainPanel) {
clone.children[1] = { node: mainPanel.node, size: rightChild.size };
}
return clone;
};
export const Automation = () => {
const workspace = useDashboardState<LayoutNode>('screens/automation', defaultLayout);
const [selection, setSelection] = usePanelChannel<AutomationSelection>('automation:selected-capability', null);
const [selected] = usePanelChannel<TaskSummary | null>('automation:selected-task', null);
const isMobile = useIsMobile();
const editing = selection?.editing ?? false;
const prevEditing = useRef(editing);
useEffect(() => {
if (editing === prevEditing.current) return;
prevEditing.current = editing;
if (editing && !hasChatPanel(workspace.value)) {
workspace.setValue(addChatPanel(workspace.value));
} else if (!editing && hasChatPanel(workspace.value)) {
workspace.setValue(removeChatPanel(workspace.value));
}
}, [editing]);
const panelComponents: PanelComponents = useMemo(
() => ({
'automation-left': AutomationSidebar,
'automation-right': AutomationRightPanel,
[CHAT_PANEL_ID]: AutomationEditChat,
'automation-list': AutomationList,
'automation-detail': AutomationDetail,
}),
[],
);
const mobilePanelId = isMobile && selection ? 'automation-right' : undefined;
const onMobileBack = useCallback(() => setSelection(null), [setSelection]);
const mobilePanelId = isMobile && selected ? 'automation-detail' : undefined;
const onMobileBack = useCallback(() => {}, []);
if (!workspace.isLoaded) return null;