refactor(webui): adopt semantic workspace actions

This commit is contained in:
2026-08-19 18:47:46 +02:00
parent e8076700b0
commit a8646e76a8
+31 -29
View File
@@ -6,15 +6,12 @@ import {
ExternalLink, ExternalLink,
ListChecks, ListChecks,
Plus, Plus,
RefreshCw,
RotateCcw, RotateCcw,
Search, Search,
XCircle XCircle
} from "lucide-react"; } from "lucide-react";
import { Link } from "react-router"; import { Link } from "react-router";
import { FormGrid, ToolbarGroup, import { FormGrid,
ActionToolbar,
AdminIconButton,
Button, Button,
DateTimeField, DateTimeField,
Dialog, Dialog,
@@ -27,6 +24,7 @@ import { FormGrid, ToolbarGroup,
SelectionListItem, SelectionListItem,
StatePanel, StatePanel,
StatusBadge, StatusBadge,
WorkspaceActionBar,
WorkspaceFrame, WorkspaceFrame,
WorkspaceLayout, WorkspaceLayout,
hasScope, hasScope,
@@ -140,26 +138,24 @@ export default function TasksPage({ settings, auth }: { settings: ApiSettings; a
helpContextId="tasks.page.inbox" helpContextId="tasks.page.inbox"
helpModuleId="tasks" helpModuleId="tasks"
primary={<> primary={<>
<ActionToolbar surface="panel-header" className="tasks-sidebar-bar"> <WorkspaceActionBar
<span className="tasks-title"><ListChecks size={18} /><strong>i18n:govoplan-tasks.work</strong></span> scope="collection-pane"
<ToolbarGroup align="end" className="tasks-toolbar-actions"> variant="collection"
<AdminIconButton refreshable
label="i18n:govoplan-tasks.refresh" reloadAction={{ onReload: () => void load(), loading: loading || busy, label: "i18n:govoplan-tasks.refresh" }}
icon={<RefreshCw size={16} aria-hidden="true" />} className="tasks-sidebar-bar"
onClick={() => void load()} contextActions={<span className="tasks-title"><ListChecks size={18} /><strong>i18n:govoplan-tasks.work</strong></span>}
disabled={loading || busy} createAction={canWrite ? (
/> <Button
{canWrite ? ( variant="primary"
<AdminIconButton
label="i18n:govoplan-tasks.create_task"
icon={<Plus size={16} aria-hidden="true" />}
onClick={() => setCreateOpen(true)} onClick={() => setCreateOpen(true)}
disabled={busy} disabled={busy}
helpContextId="tasks.action.create" helpContextId="tasks.action.create"
>
<Plus size={16} aria-hidden="true" /> i18n:govoplan-tasks.create_task
</Button>
) : undefined}
/> />
) : null}
</ToolbarGroup>
</ActionToolbar>
<FilterBar as="form" surface="control" wrap="never" className="tasks-search" onSubmit={submitSearch} role="search"> <FilterBar as="form" surface="control" wrap="never" className="tasks-search" onSubmit={submitSearch} role="search">
<Search size={15} aria-hidden="true" /> <Search size={15} aria-hidden="true" />
<input <input
@@ -203,13 +199,20 @@ export default function TasksPage({ settings, auth }: { settings: ApiSettings; a
</>} </>}
> >
<ActionToolbar surface="panel-header" className="tasks-topbar" data-help-context-id="tasks.page.detail"> <WorkspaceActionBar
<span className="tasks-detail-title"><ListChecks size={18} /><strong>{selected?.title ?? "i18n:govoplan-tasks.work_details"}</strong></span> scope="detail-pane"
<ToolbarGroup align="end" className="tasks-toolbar-actions"> variant="detail"
<DocumentationHelpLink reference={DOCUMENTATION} /> className="tasks-topbar"
{selected?.provider_id === "tasks.explicit" && canWrite ? <TaskActions item={selected} busy={busy} onAction={(action) => action === "defer" ? setDeferOpen(true) : void runAction(action)} /> : null} data-help-context-id="tasks.page.detail"
</ToolbarGroup> contextActions={<span className="tasks-detail-title"><ListChecks size={18} /><strong>{selected?.title ?? "i18n:govoplan-tasks.work_details"}</strong></span>}
</ActionToolbar> helpAction={<DocumentationHelpLink reference={DOCUMENTATION} />}
primaryActions={selected?.provider_id === "tasks.explicit" && canWrite ? (
<TaskPrimaryActions item={selected} busy={busy} onAction={(action) => action === "defer" ? setDeferOpen(true) : void runAction(action)} />
) : undefined}
destructiveActions={selected?.provider_id === "tasks.explicit" && canWrite && !["completed", "cancelled"].includes(selected.status) ? (
<Button variant="danger" onClick={() => void runAction("cancel")} disabled={busy}><XCircle size={15} /> i18n:govoplan-tasks.cancel_task</Button>
) : undefined}
/>
{error ? <DismissibleAlert tone="danger" compact resetKey={error}>{error}</DismissibleAlert> : null} {error ? <DismissibleAlert tone="danger" compact resetKey={error}>{error}</DismissibleAlert> : null}
{diagnostics.map((message) => <DismissibleAlert key={message} tone="warning" compact resetKey={message}>{message}</DismissibleAlert>)} {diagnostics.map((message) => <DismissibleAlert key={message} tone="warning" compact resetKey={message}>{message}</DismissibleAlert>)}
@@ -250,7 +253,7 @@ export default function TasksPage({ settings, auth }: { settings: ApiSettings; a
); );
} }
function TaskActions({ item, busy, onAction }: { item: WorkItem; busy: boolean; onAction: (action: "start" | "complete" | "defer" | "reopen" | "cancel") => void }) { function TaskPrimaryActions({ item, busy, onAction }: { item: WorkItem; busy: boolean; onAction: (action: "start" | "complete" | "defer" | "reopen") => void }) {
if (["completed", "cancelled"].includes(item.status)) { if (["completed", "cancelled"].includes(item.status)) {
return <Button onClick={() => onAction("reopen")} disabled={busy}><RotateCcw size={15} /> i18n:govoplan-tasks.reopen</Button>; return <Button onClick={() => onAction("reopen")} disabled={busy}><RotateCcw size={15} /> i18n:govoplan-tasks.reopen</Button>;
} }
@@ -259,7 +262,6 @@ function TaskActions({ item, busy, onAction }: { item: WorkItem; busy: boolean;
{["open", "deferred"].includes(item.status) ? <Button onClick={() => onAction("start")} disabled={busy}><CirclePlay size={15} /> i18n:govoplan-tasks.start</Button> : null} {["open", "deferred"].includes(item.status) ? <Button onClick={() => onAction("start")} disabled={busy}><CirclePlay size={15} /> i18n:govoplan-tasks.start</Button> : null}
<Button variant="primary" onClick={() => onAction("complete")} disabled={busy}><Check size={15} /> i18n:govoplan-tasks.complete</Button> <Button variant="primary" onClick={() => onAction("complete")} disabled={busy}><Check size={15} /> i18n:govoplan-tasks.complete</Button>
<Button onClick={() => onAction("defer")} disabled={busy}><CalendarClock size={15} /> i18n:govoplan-tasks.defer</Button> <Button onClick={() => onAction("defer")} disabled={busy}><CalendarClock size={15} /> i18n:govoplan-tasks.defer</Button>
<Button variant="danger" onClick={() => onAction("cancel")} disabled={busy}><XCircle size={15} /> i18n:govoplan-tasks.cancel_task</Button>
</> </>
); );
} }