refactor(webui): adopt semantic workspace actions
This commit is contained in:
@@ -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>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user