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, Eye,
FileCheck2, FileCheck2,
Plus, Plus,
RefreshCw,
Save, Save,
Send, Send,
Trash2, Trash2,
X X
} from "lucide-react"; } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { DialogSection, ToolbarGroup, ActionToolbar, import { DialogSection, ActionToolbar,
ApiError, ApiError,
ActionBlockerHint, ActionBlockerHint,
Button, Button,
@@ -30,6 +29,7 @@ import { DialogSection, ToolbarGroup, ActionToolbar,
StatePanel, StatePanel,
StatusBadge, StatusBadge,
ToggleSwitch, ToggleSwitch,
WorkspaceActionBar,
WorkspaceFrame, WorkspaceFrame,
WorkspaceLayout, WorkspaceLayout,
formatDateTime, formatDateTime,
@@ -312,10 +312,14 @@ export default function TemplatesPage({ settings, auth }: Props) {
contentLabel="Template workspace" contentLabel="Template workspace"
contentClassName="templates-workspace" contentClassName="templates-workspace"
primary={<> primary={<>
<ActionToolbar surface="panel-header"> <WorkspaceActionBar
<strong>Template library</strong> scope="collection-pane"
<IconButton label="Add template" icon={<Plus size={17} />} variant="primary" disabled={!canWrite} disabledReason={!canWrite ? TEMPLATES_I18N.writeReason : undefined} onClick={() => requestDiscard(() => setCreateOpen(true))} /> variant="collection"
</ActionToolbar> 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> <FilterBar surface="panel"><input value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Search templates" /></FilterBar>
<SelectionList variant="navigation" label="Templates"> <SelectionList variant="navigation" label="Templates">
{visibleItems.map((item) => ( {visibleItems.map((item) => (
@@ -338,25 +342,34 @@ export default function TemplatesPage({ settings, auth }: Props) {
</SelectionList> </SelectionList>
</>} </>}
> >
<ActionToolbar surface="panel-header" className="templates-workspace-toolbar"> <WorkspaceActionBar
<span className="templates-current-title"> 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> <strong>{selected?.name ?? "Select a template"}</strong>
<small>{selected ? `${typeLabel(selected.template_type)} · ${selected.revision.locale}` : ""}</small> <small>{selected ? `${typeLabel(selected.template_type)} · ${selected.revision.locale}` : ""}</small>
</span> </span>}
<ToolbarGroup align="end" className="templates-toolbar-actions"> helpAction={<DocumentationHelpLink reference={TEMPLATES_DOCUMENTATION} />}
<DocumentationHelpLink reference={TEMPLATES_DOCUMENTATION} /> primaryActions={<>
<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>
<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> <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 <SegmentedControl
value={view} value={view}
onChange={setView} onChange={setView}
options={[{ id: "definition", label: "Definition" }, { id: "preview", label: "Preview" }]} options={[{ id: "definition", label: "Definition" }, { id: "preview", label: "Preview" }]}
ariaLabel="Template workspace" 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"> <div className="templates-alerts">
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>} {error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}