From f05981dc293ccf3cbd96a6768498665e095c6841 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 31 Aug 2026 21:04:45 -0700 Subject: [PATCH] refactor(automations): extract list toolbar and drop dead list projection --- .../automations/AutomationListToolbar.tsx | 104 ++++++++++++++++++ .../automations/AutomationsListPanel.tsx | 82 +++----------- .../automations/AutomationsPageSurface.tsx | 4 - .../automations-page-test-harness.tsx | 1 - .../automations/use-automation-list-search.ts | 2 - .../use-automations-page-list-state.ts | 5 - 6 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 src/renderer/src/components/automations/AutomationListToolbar.tsx diff --git a/src/renderer/src/components/automations/AutomationListToolbar.tsx b/src/renderer/src/components/automations/AutomationListToolbar.tsx new file mode 100644 index 00000000000..c5efed566f6 --- /dev/null +++ b/src/renderer/src/components/automations/AutomationListToolbar.tsx @@ -0,0 +1,104 @@ +import React from 'react' +import { Plus, RefreshCw } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { translate } from '@/i18n/i18n' +import { cn } from '@/lib/utils' +import type { AutomationHostCatalogEntry } from './automation-host-catalog-types' +import type { AutomationListArrowKey } from './automation-list-keyboard-navigation' +import { clampAutomationListSearchQueryInput } from './automation-list-search' +import type { AutomationListFilter } from './automation-list-view' +import { AutomationListSearchField } from './AutomationListSearchField' +import { AutomationListFilterMenu } from './AutomationListFilterMenu' +import type { AutomationTemplate } from './automation-templates' + +type AutomationListToolbarProps = { + /** Focus fallback for the list: the controls that produced the rows. */ + toolbarRef: React.RefObject + listSearchQuery: string + isListSearchQueryTooLarge: boolean + onListSearchQueryChange: (query: string) => void + onSearchArrowNavigate: (key: AutomationListArrowKey) => void + onSearchEnter: () => void + listFilter: AutomationListFilter + onListFilterChange: (filter: AutomationListFilter) => void + hostEntries: readonly AutomationHostCatalogEntry[] + onRefresh: () => void + isRefreshing: boolean + openCreateDialog: (template?: AutomationTemplate) => void + canCreateAutomation: boolean +} + +export function AutomationListToolbar({ + toolbarRef, + listSearchQuery, + isListSearchQueryTooLarge, + onListSearchQueryChange, + onSearchArrowNavigate, + onSearchEnter, + listFilter, + onListFilterChange, + hostEntries, + onRefresh, + isRefreshing, + openCreateDialog, + canCreateAutomation +}: AutomationListToolbarProps): React.JSX.Element { + return ( +
+
+ + onListSearchQueryChange(clampAutomationListSearchQueryInput(query)) + } + onClear={() => onListSearchQueryChange('')} + onArrowNavigate={onSearchArrowNavigate} + onEnter={onSearchEnter} + /> + + + + + + + {translate( + 'auto.components.automations.AutomationsPage.19a6e30eae', + 'Refresh automations' + )} + + +
+ +
+ ) +} diff --git a/src/renderer/src/components/automations/AutomationsListPanel.tsx b/src/renderer/src/components/automations/AutomationsListPanel.tsx index 5659f324cf9..fa6cb5891c4 100644 --- a/src/renderer/src/components/automations/AutomationsListPanel.tsx +++ b/src/renderer/src/components/automations/AutomationsListPanel.tsx @@ -1,10 +1,6 @@ import React, { useRef } from 'react' -import { Plus, RefreshCw } from 'lucide-react' -import { Button } from '@/components/ui/button' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' import type { - Automation, AutomationRun, ExternalAutomationAction, ExternalAutomationJob, @@ -19,7 +15,6 @@ import type { RuntimeStatus } from '../../../../shared/runtime-types' import type { TaskSourceHostAvailability } from '../task-source-context-summary' import type { AutomationRowAction } from './automation-captured-owner' import type { AutomationHostTarget } from './automation-host-client' -import { clampAutomationListSearchQueryInput } from './automation-list-search' import { createAutomationListEnterHandler, getAutomationListArrowNavigationTarget, @@ -27,8 +22,7 @@ import { } from './automation-list-keyboard-navigation' import type { AutomationListRow } from './automation-list-row-identity' import type { AutomationPaneTab } from './automation-page-state' -import { AutomationListSearchField } from './AutomationListSearchField' -import { AutomationListFilterMenu, AutomationListFilterPills } from './AutomationListFilterMenu' +import { AutomationListFilterPills } from './AutomationListFilterMenu' import { isAutomationListFilterActive, type AutomationListFilter } from './automation-list-view' import { automationHostFilterStableKey } from '../../../../shared/automation-host-filter' import type { AutomationTemplate } from './automation-templates' @@ -48,6 +42,7 @@ import { LIST_TABLE_CONTAINER_CLASS } from '@/lib/list-table-layout' import { translate } from '@/i18n/i18n' import { AutomationTemplateEmptyState } from './AutomationTemplateEmptyState' import { AutomationListTableHeader } from './AutomationListTableHeader' +import { AutomationListToolbar } from './AutomationListToolbar' const TEMPLATE_EMPTY_STATES: ReadonlySet = new Set(['host-empty', 'all-hosts-empty']) const EMPTY_AUTOMATION_RUNS: ReadonlyMap = new Map() @@ -55,9 +50,6 @@ const EMPTY_AUTOMATION_RUNS: ReadonlyMap = new Map() type AutomationsListPanelProps = { hasListItems: boolean hasFilteredListItems: boolean - /** Compatibility projections for consumers that still inspect bare rows. */ - isListSearchActive?: boolean - filteredAutomations?: readonly Automation[] listSearchQuery: string isListSearchQueryTooLarge: boolean onListSearchQueryChange: (query: string) => void @@ -289,61 +281,21 @@ export function AutomationsListPanel(props: AutomationsListPanelProps): React.JS className="flex min-h-0 flex-1 flex-col overflow-hidden px-3 pb-4 md:px-5" data-contextual-tour-target="automations-list" > -
-
- - onListSearchQueryChange(clampAutomationListSearchQueryInput(query)) - } - onClear={() => onListSearchQueryChange('')} - onArrowNavigate={handleSearchArrowNavigate} - onEnter={handleSearchEnter} - /> - - - - - - - {translate( - 'auto.components.automations.AutomationsPage.19a6e30eae', - 'Refresh automations' - )} - - -
- -
+ {listFilterActive || legacyScopeStableKey !== null ? (
diff --git a/src/renderer/src/components/automations/AutomationsPageSurface.tsx b/src/renderer/src/components/automations/AutomationsPageSurface.tsx index d81b26d65c5..512add800a3 100644 --- a/src/renderer/src/components/automations/AutomationsPageSurface.tsx +++ b/src/renderer/src/components/automations/AutomationsPageSurface.tsx @@ -90,10 +90,8 @@ export function AutomationsPageSurface({ hostCatalog, hasListItems, hasFilteredListItems, - isListSearchActive, isListSearchQueryTooLarge, filteredRows, - filteredAutomations, filteredExternalAutomationEntries, selected, selectedRow, @@ -319,7 +317,6 @@ export function AutomationsPageSurface({ hasFilteredListItems={hasFilteredListItems} listSearchQuery={listSearchQuery} isListSearchQueryTooLarge={isListSearchQueryTooLarge} - isListSearchActive={isListSearchActive} onListSearchQueryChange={setListSearchQuery} listFilter={listFilter} onListFilterChange={onListFilterChange} @@ -337,7 +334,6 @@ export function AutomationsPageSurface({ } }} filteredRows={filteredRows} - filteredAutomations={filteredAutomations} filteredExternalAutomationEntries={filteredExternalAutomationEntries} selectedRowKey={selectedRow?.key ?? null} selectedExternalKey={local.selectedExternalKey} diff --git a/src/renderer/src/components/automations/automations-page-test-harness.tsx b/src/renderer/src/components/automations/automations-page-test-harness.tsx index ae01653093c..d9fd1088bf7 100644 --- a/src/renderer/src/components/automations/automations-page-test-harness.tsx +++ b/src/renderer/src/components/automations/automations-page-test-harness.tsx @@ -39,7 +39,6 @@ export const RUNTIME_REPO_ID = RUNTIME_REPO_ID_FIXTURE export const RUNTIME_WORKSPACE_ID = RUNTIME_WORKSPACE_ID_FIXTURE export type ListPanelProps = { - filteredAutomations: Automation[] filteredExternalAutomationEntries: ExternalAutomationListEntry[] selectedExternal: ExternalAutomationListEntry | null openEditExternalDialog: ( diff --git a/src/renderer/src/components/automations/use-automation-list-search.ts b/src/renderer/src/components/automations/use-automation-list-search.ts index 435e534962e..28ddc5a5c89 100644 --- a/src/renderer/src/components/automations/use-automation-list-search.ts +++ b/src/renderer/src/components/automations/use-automation-list-search.ts @@ -61,7 +61,6 @@ export function useAutomationListSearch({ selectExternalKey: (externalKey: string | null) => void }): { isListSearchQueryTooLarge: boolean - isListSearchActive: boolean filteredRows: readonly AutomationListRow[] filteredExternalAutomationEntries: readonly ExternalAutomationListEntry[] hasListItems: boolean @@ -200,7 +199,6 @@ export function useAutomationListSearch({ return { isListSearchQueryTooLarge, - isListSearchActive, filteredRows, filteredExternalAutomationEntries, hasListItems: hostRowCount > 0, diff --git a/src/renderer/src/components/automations/use-automations-page-list-state.ts b/src/renderer/src/components/automations/use-automations-page-list-state.ts index 6c81c25a737..65cce90ffc2 100644 --- a/src/renderer/src/components/automations/use-automations-page-list-state.ts +++ b/src/renderer/src/components/automations/use-automations-page-list-state.ts @@ -85,7 +85,6 @@ export function useAutomationsPageListState({ const selected = selectedRow?.automation ?? null const { isListSearchQueryTooLarge, - isListSearchActive, filteredRows, filteredExternalAutomationEntries, hasListItems, @@ -145,11 +144,7 @@ export function useAutomationsPageListState({ selected, selectedAutomationRunsWithWorkspaceNames, isListSearchQueryTooLarge, - isListSearchActive, filteredRows, - // Keep the legacy projection available to integrations that still consume - // bare automations; the table itself uses host-qualified rows. - filteredAutomations: filteredRows.map((row) => row.automation), filteredExternalAutomationEntries, hasListItems, hasFilteredListItems,