refactor(webui): adopt semantic workspace actions

This commit is contained in:
2026-08-19 18:47:46 +02:00
parent 244c0ee4c1
commit f49ad8eff4
+37 -36
View File
@@ -14,7 +14,6 @@ import {
GitFork, GitFork,
ListChecks, ListChecks,
Plus, Plus,
RefreshCw,
RotateCcw, RotateCcw,
Save, Save,
Settings2, Settings2,
@@ -23,7 +22,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { ReactFlowProvider } from "@xyflow/react"; import { ReactFlowProvider } from "@xyflow/react";
import { useSearchParams } from "react-router"; import { useSearchParams } from "react-router";
import { ToolbarGroup, ActionToolbar, import { ActionToolbar,
Button, Button,
ConfirmDialog, ConfirmDialog,
ContentGrid, ContentGrid,
@@ -46,6 +45,7 @@ import { ToolbarGroup, ActionToolbar,
StatePanel, StatePanel,
StatusBadge, StatusBadge,
ToggleSwitch, ToggleSwitch,
WorkspaceActionBar,
WorkspaceFrame, WorkspaceFrame,
WorkspaceLayout, WorkspaceLayout,
hasScope, hasScope,
@@ -111,7 +111,7 @@ export default function WorkflowPage({
settings: ApiSettings; settings: ApiSettings;
auth: AuthInfo; auth: AuthInfo;
}) { }) {
const { requestNavigation } = useUnsavedChanges(); const { requestDiscard, requestNavigation } = useUnsavedChanges();
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const requestedDefinitionId = searchParams.get("definition"); const requestedDefinitionId = searchParams.get("definition");
const requestedRunId = searchParams.get("run"); const requestedRunId = searchParams.get("run");
@@ -606,20 +606,13 @@ export default function WorkflowPage({
contentLabel="Workflow editor" contentLabel="Workflow editor"
contentClassName="workflow-workspace" contentClassName="workflow-workspace"
primary={<> primary={<>
<ActionToolbar surface="panel-header"> <WorkspaceActionBar
scope="collection-pane"
variant="collection"
refreshable
reloadAction={{ onReload: () => void reload(draft?.id), loading: loading || working }}
contextActions={<>
<strong>Workflows</strong> <strong>Workflows</strong>
<ToolbarGroup align="end" className="workflow-toolbar-actions">
<DocumentationHelpLink
reference={{ topicId: "workflow.editor", documentationType: "user" }}
label="Open Workflow documentation"
/>
<IconButton
label="Refresh"
icon={<RefreshCw size={16} />}
variant="ghost"
onClick={() => void reload(draft?.id)}
disabled={loading || working}
/>
{hasScope(auth, "workflow:instance:admin") ? ( {hasScope(auth, "workflow:instance:admin") ? (
<IconButton <IconButton
label="Reconcile module standards" label="Reconcile module standards"
@@ -629,16 +622,20 @@ export default function WorkflowPage({
disabled={loading || working} disabled={loading || working}
/> />
) : null} ) : null}
<IconButton </>}
helpAction={<DocumentationHelpLink
reference={{ topicId: "workflow.editor", documentationType: "user" }}
label="Open Workflow documentation"
/>}
createAction={<IconButton
label="New workflow" label="New workflow"
icon={<Plus size={17} />} icon={<Plus size={17} />}
variant="primary" variant="primary"
onClick={createNew} onClick={createNew}
disabled={!canWrite} disabled={!canWrite}
disabledReason={!canWrite ? "Workflow definition write permission is required." : undefined} disabledReason={!canWrite ? "Workflow definition write permission is required." : undefined}
/>}
/> />
</ToolbarGroup>
</ActionToolbar>
<FilterBar surface="panel"> <FilterBar surface="panel">
<input <input
type="search" type="search"
@@ -683,8 +680,12 @@ export default function WorkflowPage({
> >
{draft && displayedGraph ? ( {draft && displayedGraph ? (
<> <>
<ActionToolbar surface="panel-header" className="workflow-workspace-toolbar"> <WorkspaceActionBar
<span className="workflow-identity-fields"> scope="editor-pane"
variant="editor"
state={working ? "saving" : dirty && !draft.name.trim() ? "invalid" : dirty ? "dirty" : "clean"}
className="workflow-workspace-toolbar"
contextActions={<span className="workflow-identity-fields">
<input <input
className="workflow-name-input" className="workflow-name-input"
value={draft.name} value={draft.name}
@@ -706,8 +707,8 @@ export default function WorkflowPage({
placeholder="Description" placeholder="Description"
aria-label="Workflow description" aria-label="Workflow description"
/> />
</span> </span>}
<span className="workflow-command-bar"> primaryActions={<span className="workflow-command-bar">
{draft.id ? ( {draft.id ? (
<select <select
className="workflow-revision-select" className="workflow-revision-select"
@@ -851,16 +852,8 @@ export default function WorkflowPage({
<CheckCircle2 size={16} /> Activate <CheckCircle2 size={16} /> Activate
</Button> </Button>
) : null} ) : null}
<Button </span>}
variant="primary" destructiveActions={draft.id && draft.standard?.kind !== "baseline" ? (
onClick={() => void saveDraft()}
disabled={
!canEdit || !dirty || working || readOnly
}
>
<Save size={16} /> Save
</Button>
{draft.id && draft.standard?.kind !== "baseline" ? (
<IconButton <IconButton
label="Delete workflow" label="Delete workflow"
icon={<Trash2 size={16} />} icon={<Trash2 size={16} />}
@@ -868,9 +861,17 @@ export default function WorkflowPage({
onClick={() => setDeleteOpen(true)} onClick={() => setDeleteOpen(true)}
disabled={!canEdit || working} disabled={!canEdit || working}
/> />
) : null} ) : undefined}
</span> discardAction={{
</ActionToolbar> label: <><RotateCcw size={16} /> Discard</>,
onClick: () => requestDiscard(() => void reload(draft.id))
}}
saveAction={{
label: <><Save size={16} /> Save</>,
onClick: () => void saveDraft(),
disabled: !canEdit || readOnly
}}
/>
<div className="workflow-alerts"> <div className="workflow-alerts">
{error ? ( {error ? (
<DismissibleAlert tone="danger" resetKey={error}> <DismissibleAlert tone="danger" resetKey={error}>