refactor(webui): adopt semantic workspace actions

This commit is contained in:
2026-08-19 18:47:46 +02:00
parent 0a606184b1
commit e932a3d0f7
2 changed files with 39 additions and 29 deletions
@@ -1,7 +1,7 @@
import { DescriptionList } from "@govoplan/core-webui"; import { DescriptionList } from "@govoplan/core-webui";
import { Download, FileJson, Play, ShieldCheck } from "lucide-react"; import { Download, FileJson, Play, ShieldCheck } from "lucide-react";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { ContentGrid, ActionToolbar, import { ContentGrid,
Button, Button,
Card, Card,
DismissibleAlert, DismissibleAlert,
@@ -9,6 +9,7 @@ import { ContentGrid, ActionToolbar,
MetricCard, MetricCard,
StatePanel, StatePanel,
StatusBadge, StatusBadge,
WorkspaceActionBar,
hasScope, hasScope,
type ApiSettings, type ApiSettings,
type AuthInfo type AuthInfo
@@ -176,15 +177,20 @@ export function ProviderReportWorkspace({ settings, auth, report }: {
</div> </div>
{execution ? {execution ?
<> <>
<ActionToolbar surface="panel-header" className="reporting-output-toolbar"> <WorkspaceActionBar
<span>Generated {formatDateTime(execution.generated_at)}</span> scope="detail-pane"
variant="detail"
className="reporting-output-toolbar"
contextActions={<span>Generated {formatDateTime(execution.generated_at)}</span>}
primaryActions={<>
{report.export_formats.includes("csv") && {report.export_formats.includes("csv") &&
<IconButton label="Download CSV" icon={<Download size={17} />} variant="ghost" onClick={() => void download("csv")} /> <IconButton label="Download CSV" icon={<Download size={17} />} variant="ghost" onClick={() => void download("csv")} />
} }
{report.export_formats.includes("json") && {report.export_formats.includes("json") &&
<IconButton label="Download JSON" icon={<FileJson size={17} />} variant="ghost" onClick={() => void download("json")} /> <IconButton label="Download JSON" icon={<FileJson size={17} />} variant="ghost" onClick={() => void download("json")} />
} }
</ActionToolbar> </>}
/>
<div className="reporting-provider-output"> <div className="reporting-provider-output">
<ProviderResult execution={execution} /> <ProviderResult execution={execution} />
</div> </div>
+21 -17
View File
@@ -7,7 +7,6 @@ import {
FolderOutput, FolderOutput,
History, History,
Play, Play,
RefreshCw,
Save, Save,
Search, Search,
SlidersHorizontal, SlidersHorizontal,
@@ -19,7 +18,7 @@ import {
useState, useState,
type FormEvent type FormEvent
} from "react"; } from "react";
import { FormGrid, ActionToolbar, import { FormGrid,
Button, Button,
DataGrid, DataGrid,
Dialog, Dialog,
@@ -37,6 +36,7 @@ import { FormGrid, ActionToolbar,
StatusBadge, StatusBadge,
ToggleSwitch, ToggleSwitch,
hasScope, hasScope,
WorkspaceActionBar,
WorkspaceFrame, WorkspaceFrame,
type DataGridColumn, type DataGridColumn,
type PlatformRouteContext type PlatformRouteContext
@@ -256,7 +256,13 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
return ( return (
<main className="reporting-page"> <main className="reporting-page">
<WorkspaceFrame className="reporting-shell" label="Reporting workspace" interfaceId="reporting.workspace" helpContextId="reporting.page.workspace" helpModuleId="reporting"> <WorkspaceFrame className="reporting-shell" label="Reporting workspace" interfaceId="reporting.workspace" helpContextId="reporting.page.workspace" helpModuleId="reporting">
<ActionToolbar surface="panel-header" className="reporting-toolbar"> <WorkspaceActionBar
scope="workspace"
variant="collection"
refreshable
reloadAction={{ onReload: () => void reload(), loading, label: "Reload reports" }}
className="reporting-toolbar"
contextActions={<>
<FilterBar as="form" surface="control" wrap="never" width="compact" className="reporting-search" onSubmit={submitSearch}> <FilterBar as="form" surface="control" wrap="never" width="compact" className="reporting-search" onSubmit={submitSearch}>
<Search size={17} aria-hidden="true" /> <Search size={17} aria-hidden="true" />
<input <input
@@ -267,17 +273,12 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
/> />
</FilterBar> </FilterBar>
<span className="reporting-count">{reports.length + providerReports.length} reports</span> <span className="reporting-count">{reports.length + providerReports.length} reports</span>
<DocumentationHelpLink </>}
helpAction={<DocumentationHelpLink
reference={{ topicId: "reporting.governed-bi", documentationType: "user" }} reference={{ topicId: "reporting.governed-bi", documentationType: "user" }}
label="Open reporting documentation" label="Open reporting documentation"
/>}
/> />
<IconButton
label="Reload reports"
icon={<RefreshCw size={17} />}
variant="ghost"
onClick={() => void reload()}
/>
</ActionToolbar>
{error && {error &&
<DismissibleAlert tone="danger" resetKey={error}> <DismissibleAlert tone="danger" resetKey={error}>
{error} {error}
@@ -345,8 +346,11 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
onQueryChange={setQuery} onQueryChange={setQuery}
onParametersChange={setParameters} onParametersChange={setParameters}
/> />
<ActionToolbar surface="panel-header" className="reporting-output-toolbar"> <WorkspaceActionBar
<SegmentedControl scope="detail-pane"
variant="detail"
className="reporting-output-toolbar"
contextActions={<SegmentedControl
ariaLabel="Report output" ariaLabel="Report output"
value={outputMode} value={outputMode}
onChange={setOutputMode} onChange={setOutputMode}
@@ -354,8 +358,8 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
{ id: "visual", label: <><BarChart3 size={15} /> Visual</> }, { id: "visual", label: <><BarChart3 size={15} /> Visual</> },
{ id: "table", label: <><Table2 size={15} /> Table</> } { id: "table", label: <><Table2 size={15} /> Table</> }
]} ]}
/> />}
{execution && primaryActions={execution ?
<> <>
<span>{execution.total_rows} rows{execution.truncated ? " (truncated)" : ""}</span> <span>{execution.total_rows} rows{execution.truncated ? " (truncated)" : ""}</span>
<IconButton label="Download CSV" icon={<Download size={17} />} variant="ghost" onClick={() => void downloadExecution(settings, execution.execution_id, "csv").catch((reason) => setError(message(reason)))} /> <IconButton label="Download CSV" icon={<Download size={17} />} variant="ghost" onClick={() => void downloadExecution(settings, execution.execution_id, "csv").catch((reason) => setError(message(reason)))} />
@@ -364,8 +368,8 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
<IconButton label="Publish report" icon={<FolderOutput size={17} />} variant="ghost" onClick={() => setPublishDialogOpen(true)} /> <IconButton label="Publish report" icon={<FolderOutput size={17} />} variant="ghost" onClick={() => setPublishDialogOpen(true)} />
} }
</> </>
} : undefined}
</ActionToolbar> />
<div className="reporting-output"> <div className="reporting-output">
{!execution && <StatePanel size="fill" description="Run the report or select a previous execution." />} {!execution && <StatePanel size="fill" description="Run the report or select a previous execution." />}
{execution && outputMode === "visual" && <ReportVisual execution={execution} onDrill={execution.query.mode === "detail" ? undefined : drill} />} {execution && outputMode === "visual" && <ReportVisual execution={execution} onDrill={execution.query.mode === "detail" ? undefined : drill} />}