refactor(webui): adopt semantic workspace actions

This commit is contained in:
2026-08-19 18:47:45 +02:00
parent 21da2e1ad4
commit d12e0bce7d
+34 -57
View File
@@ -26,7 +26,7 @@ import {
TriangleAlert, TriangleAlert,
Upload Upload
} from "lucide-react"; } from "lucide-react";
import { DialogSection, ToolbarGroup, ActionToolbar, import { DialogSection, ActionToolbar,
ActionBlockerHint, ActionBlockerHint,
Button, Button,
ConfirmDialog, ConfirmDialog,
@@ -51,6 +51,7 @@ import { DialogSection, ToolbarGroup, ActionToolbar,
StatePanel, StatePanel,
StatusBadge, StatusBadge,
ToggleSwitch, ToggleSwitch,
WorkspaceActionBar,
WorkspaceFrame, WorkspaceFrame,
WorkspaceLayout, WorkspaceLayout,
hasScope, hasScope,
@@ -608,27 +609,21 @@ export default function DataflowPage({ settings, auth }: { settings: ApiSettings
contentLabel="Pipeline editor" contentLabel="Pipeline editor"
contentClassName="dataflow-workspace" contentClassName="dataflow-workspace"
primary={<> primary={<>
<ActionToolbar surface="panel-header"> <WorkspaceActionBar
<strong>Pipelines</strong> scope="collection-pane"
<ToolbarGroup align="end" className="dataflow-toolbar-actions"> variant="collection"
<IconButton refreshable
label="Refresh pipelines" reloadAction={{ onReload: () => void loadPipelines(draft?.id), loading, label: "Refresh pipelines" }}
icon={<RefreshCw size={16} />} contextActions={<strong>Pipelines</strong>}
variant="ghost" createAction={<IconButton
onClick={() => requestNavigation(() => void loadPipelines(draft?.id))}
disabled={loading}
disabledReason={loading ? DATAFLOW_I18N.loading : undefined}
/>
<IconButton
label="New pipeline" label="New pipeline"
icon={<Plus size={17} />} icon={<Plus size={17} />}
variant="primary" variant="primary"
onClick={createNew} onClick={createNew}
disabled={!canWrite} disabled={!canWrite}
disabledReason={!canWrite ? DATAFLOW_I18N.writeReason : undefined} disabledReason={!canWrite ? DATAFLOW_I18N.writeReason : undefined}
/>}
/> />
</ToolbarGroup>
</ActionToolbar>
<FilterBar surface="panel"> <FilterBar surface="panel">
<input <input
type="search" type="search"
@@ -662,8 +657,12 @@ export default function DataflowPage({ settings, auth }: { settings: ApiSettings
> >
{draft ? ( {draft ? (
<> <>
<ActionToolbar surface="panel-header" className="dataflow-workspace-toolbar"> <WorkspaceActionBar
<div className="dataflow-identity-fields"> scope="editor-pane"
variant="editor"
state={saving ? "saving" : dirty && !draft.name.trim() ? "invalid" : dirty ? "dirty" : "clean"}
className="dataflow-workspace-toolbar"
contextActions={<div className="dataflow-identity-fields">
<input <input
className="dataflow-name-input" className="dataflow-name-input"
value={draft.name} value={draft.name}
@@ -691,9 +690,9 @@ export default function DataflowPage({ settings, auth }: { settings: ApiSettings
</option> </option>
<option value="archived">Archived</option> <option value="archived">Archived</option>
</select> </select>
</div> </div>}
<div className="dataflow-command-bar"> helpAction={<DocumentationHelpLink reference={DATAFLOW_DOCUMENTATION} />}
<DocumentationHelpLink reference={DATAFLOW_DOCUMENTATION} /> primaryActions={<div className="dataflow-command-bar">
<SegmentedControl<EditorMode> <SegmentedControl<EditorMode>
ariaLabel="Pipeline editor mode" ariaLabel="Pipeline editor mode"
options={[ options={[
@@ -777,50 +776,28 @@ export default function DataflowPage({ settings, auth }: { settings: ApiSettings
disabledReason={!canViewTriggers ? DATAFLOW_I18N.writeReason : undefined} disabledReason={!canViewTriggers ? DATAFLOW_I18N.writeReason : undefined}
/> />
) : null} ) : null}
<IconButton </div>}
label="Discard changes" destructiveActions={draft.id ? (
icon={<RotateCcw size={16} />}
variant="ghost"
onClick={() => requestDiscard(() => undefined)}
disabled={!dirty || saving}
disabledReason={
saving
? DATAFLOW_I18N.working
: !dirty
? DATAFLOW_I18N.noChanges
: undefined
}
/>
{draft.id ? (
<IconButton <IconButton
label="Delete pipeline" label="Delete pipeline"
icon={<Trash2 size={16} />} icon={<Trash2 size={16} />}
variant="danger" variant="danger"
onClick={() => setDeleteOpen(true)} onClick={() => setDeleteOpen(true)}
disabled={!canEdit || saving} disabled={!canEdit || saving}
disabledReason={ disabledReason={saving ? DATAFLOW_I18N.working : !canEdit ? editBlockedReason : undefined}
saving ? DATAFLOW_I18N.working : !canEdit ? editBlockedReason : undefined />
} ) : undefined}
discardAction={{
label: <><RotateCcw size={16} /> Discard</>,
onClick: () => requestDiscard(() => undefined)
}}
saveAction={{
label: <><Save size={16} /> {saving ? "Saving..." : "Save"}</>,
onClick: () => void saveDraft(),
disabled: working || !canEdit,
disabledReason: working ? DATAFLOW_I18N.working : !canEdit ? editBlockedReason : undefined
}}
/> />
) : null}
<Button
variant="primary"
onClick={() => void saveDraft()}
disabled={saving || working || !dirty || !canEdit}
disabledReason={
saving || working
? DATAFLOW_I18N.working
: !canEdit
? editBlockedReason
: !dirty
? DATAFLOW_I18N.noChanges
: undefined
}
>
<Save size={16} /> {saving ? "Saving..." : "Save"}
</Button>
</div>
</ActionToolbar>
{error ? ( {error ? (
<DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert> <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>
) : null} ) : null}