refactor(webui): adopt semantic workspace actions

This commit is contained in:
2026-08-19 18:47:46 +02:00
parent 58be2482ec
commit 1c12359750
+29 -16
View File
@@ -3,14 +3,13 @@ import {
Eye,
FileCheck2,
Plus,
RefreshCw,
Save,
Send,
Trash2,
X
} from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DialogSection, ToolbarGroup, ActionToolbar,
import { DialogSection, ActionToolbar,
ApiError,
ActionBlockerHint,
Button,
@@ -30,6 +29,7 @@ import { DialogSection, ToolbarGroup, ActionToolbar,
StatePanel,
StatusBadge,
ToggleSwitch,
WorkspaceActionBar,
WorkspaceFrame,
WorkspaceLayout,
formatDateTime,
@@ -312,10 +312,14 @@ export default function TemplatesPage({ settings, auth }: Props) {
contentLabel="Template workspace"
contentClassName="templates-workspace"
primary={<>
<ActionToolbar surface="panel-header">
<strong>Template library</strong>
<IconButton label="Add template" icon={<Plus size={17} />} variant="primary" disabled={!canWrite} disabledReason={!canWrite ? TEMPLATES_I18N.writeReason : undefined} onClick={() => requestDiscard(() => setCreateOpen(true))} />
</ActionToolbar>
<WorkspaceActionBar
scope="collection-pane"
variant="collection"
refreshable
reloadAction={{ onReload: () => void reload(selectedId), loading: loading || busy }}
contextActions={<strong>Template library</strong>}
createAction={<IconButton label="Add template" icon={<Plus size={17} />} variant="primary" disabled={!canWrite} disabledReason={!canWrite ? TEMPLATES_I18N.writeReason : undefined} onClick={() => requestDiscard(() => setCreateOpen(true))} />}
/>
<FilterBar surface="panel"><input value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Search templates" /></FilterBar>
<SelectionList variant="navigation" label="Templates">
{visibleItems.map((item) => (
@@ -338,25 +342,34 @@ export default function TemplatesPage({ settings, auth }: Props) {
</SelectionList>
</>}
>
<ActionToolbar surface="panel-header" className="templates-workspace-toolbar">
<span className="templates-current-title">
<WorkspaceActionBar
scope="editor-pane"
variant="editor"
state={busy ? "saving" : dirty ? "dirty" : "clean"}
className="templates-workspace-toolbar"
contextActions={<span className="templates-current-title">
<strong>{selected?.name ?? "Select a template"}</strong>
<small>{selected ? `${typeLabel(selected.template_type)} · ${selected.revision.locale}` : ""}</small>
</span>
<ToolbarGroup align="end" className="templates-toolbar-actions">
<DocumentationHelpLink reference={TEMPLATES_DOCUMENTATION} />
<IconButton label="Discard and reload" icon={<RefreshCw size={17} />} disabled={loading || busy} disabledReason={loading ? TEMPLATES_I18N.loading : busy ? TEMPLATES_I18N.busy : undefined} onClick={() => requestDiscard(() => void reload(selectedId))} />
<Button variant="primary" disabled={!selected || readOnly || !dirty || busy} disabledReason={busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : !dirty ? TEMPLATES_I18N.noChanges : undefined} onClick={() => void save()}><Save size={16} /> Save revision</Button>
</span>}
helpAction={<DocumentationHelpLink reference={TEMPLATES_DOCUMENTATION} />}
primaryActions={<>
<Button disabled={!selected || !canPublish || dirty || busy} disabledReason={busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : !canPublish ? TEMPLATES_I18N.publishReason : dirty ? TEMPLATES_I18N.saveBeforeAction : undefined} onClick={() => setPublishOpen(true)}><FileCheck2 size={16} /> Publish</Button>
<IconButton label="Delete template" icon={<Trash2 size={17} />} variant="danger" disabled={!selected || readOnly} disabledReason={!selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : undefined} onClick={() => setDeleteOpen(true)} />
<SegmentedControl
value={view}
onChange={setView}
options={[{ id: "definition", label: "Definition" }, { id: "preview", label: "Preview" }]}
ariaLabel="Template workspace"
/>
</ToolbarGroup>
</ActionToolbar>
</>}
destructiveActions={<IconButton label="Delete template" icon={<Trash2 size={17} />} variant="danger" disabled={!selected || readOnly} disabledReason={!selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : undefined} onClick={() => setDeleteOpen(true)} />}
discardAction={{ label: "Discard and reload", onClick: () => requestDiscard(() => void reload(selectedId)), disabled: !selected }}
saveAction={{
label: <><Save size={16} /> Save revision</>,
disabled: !selected || readOnly || busy,
disabledReason: busy ? TEMPLATES_I18N.busy : !selected ? TEMPLATES_I18N.noSelection : readOnly ? (canWrite ? TEMPLATES_I18N.readOnlyReason : TEMPLATES_I18N.writeReason) : undefined,
onClick: () => void save()
}}
/>
<div className="templates-alerts">
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}