Adopt shared WebUI structural primitives
This commit is contained in:
@@ -7,6 +7,7 @@ import { ContentGrid, ActionToolbar,
|
|||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
IconButton,
|
IconButton,
|
||||||
MetricCard,
|
MetricCard,
|
||||||
|
StatePanel,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
hasScope,
|
hasScope,
|
||||||
type ApiSettings,
|
type ApiSettings,
|
||||||
@@ -175,7 +176,7 @@ export function ProviderReportWorkspace({ settings, auth, report }: {
|
|||||||
</div>
|
</div>
|
||||||
{execution ?
|
{execution ?
|
||||||
<>
|
<>
|
||||||
<ActionToolbar className="reporting-output-toolbar">
|
<ActionToolbar surface="panel-header" className="reporting-output-toolbar">
|
||||||
<span>Generated {formatDateTime(execution.generated_at)}</span>
|
<span>Generated {formatDateTime(execution.generated_at)}</span>
|
||||||
{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")} />
|
||||||
@@ -188,7 +189,7 @@ export function ProviderReportWorkspace({ settings, auth, report }: {
|
|||||||
<ProviderResult execution={execution} />
|
<ProviderResult execution={execution} />
|
||||||
</div>
|
</div>
|
||||||
</> :
|
</> :
|
||||||
<div className="reporting-empty">Select the parameters and run this governed report.</div>
|
<StatePanel size="fill" description="Select the parameters and run this governed report." />
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,13 +25,19 @@ import { FormGrid, ActionToolbar,
|
|||||||
Dialog,
|
Dialog,
|
||||||
DocumentationHelpLink,
|
DocumentationHelpLink,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
|
FilterBar,
|
||||||
IconButton,
|
IconButton,
|
||||||
LoadingIndicator,
|
LoadingIndicator,
|
||||||
PageScrollViewport,
|
PageScrollViewport,
|
||||||
|
StatePanel,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
|
SelectionList,
|
||||||
|
SelectionListItem,
|
||||||
|
SelectionListItemContent,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
hasScope,
|
hasScope,
|
||||||
|
WorkspaceFrame,
|
||||||
type DataGridColumn,
|
type DataGridColumn,
|
||||||
type PlatformRouteContext
|
type PlatformRouteContext
|
||||||
} from "@govoplan/core-webui";
|
} from "@govoplan/core-webui";
|
||||||
@@ -249,9 +255,9 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="reporting-page">
|
<main className="reporting-page">
|
||||||
<div className="reporting-shell">
|
<WorkspaceFrame className="reporting-shell" label="Reporting workspace" interfaceId="reporting.workspace" helpContextId="reporting.page.workspace" helpModuleId="reporting">
|
||||||
<ActionToolbar className="reporting-toolbar">
|
<ActionToolbar surface="panel-header" className="reporting-toolbar">
|
||||||
<form 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
|
||||||
value={search}
|
value={search}
|
||||||
@@ -259,7 +265,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
aria-label="Search reports"
|
aria-label="Search reports"
|
||||||
placeholder="Search reports"
|
placeholder="Search reports"
|
||||||
/>
|
/>
|
||||||
</form>
|
</FilterBar>
|
||||||
<span className="reporting-count">{reports.length + providerReports.length} reports</span>
|
<span className="reporting-count">{reports.length + providerReports.length} reports</span>
|
||||||
<DocumentationHelpLink
|
<DocumentationHelpLink
|
||||||
reference={{ topicId: "reporting.governed-bi", documentationType: "user" }}
|
reference={{ topicId: "reporting.governed-bi", documentationType: "user" }}
|
||||||
@@ -280,38 +286,32 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
<div className="reporting-workspace">
|
<div className="reporting-workspace">
|
||||||
<PageScrollViewport className="reporting-catalogue">
|
<PageScrollViewport className="reporting-catalogue">
|
||||||
{loading && <LoadingIndicator label="Loading reports" />}
|
{loading && <LoadingIndicator label="Loading reports" />}
|
||||||
{!loading && reports.length + providerReports.length === 0 && <div className="reporting-empty">No active reports are available.</div>}
|
{!loading && reports.length + providerReports.length === 0 && <StatePanel size="compact" description="No active reports are available." />}
|
||||||
<div className="reporting-report-list" role="list">
|
<SelectionList variant="navigation" label="Reports">
|
||||||
{providerReports.length > 0 && <div className="reporting-list-heading">Module reports</div>}
|
{providerReports.length > 0 && <div className="reporting-list-heading" role="presentation">Module reports</div>}
|
||||||
{providerReports.map((item) => {
|
{providerReports.map((item) => {
|
||||||
const key = `${item.provider_id}:${item.report_id}`;
|
const key = `${item.provider_id}:${item.report_id}`;
|
||||||
return (
|
return (
|
||||||
<button
|
<SelectionListItem
|
||||||
type="button"
|
|
||||||
role="listitem"
|
|
||||||
key={key}
|
key={key}
|
||||||
className={`reporting-report-row${selectedProviderKey === key ? " is-selected" : ""}`}
|
selected={selectedProviderKey === key}
|
||||||
onClick={() => { setSelectedProviderKey(key); setSelectedId(""); }}>
|
onClick={() => { setSelectedProviderKey(key); setSelectedId(""); }}>
|
||||||
<BarChart3 size={17} aria-hidden="true" />
|
<SelectionListItemContent leading={<BarChart3 size={17} />} title={item.title} description={`${item.provider_id} · ${item.revision}`} />
|
||||||
<span><strong>{item.title}</strong><small>{item.provider_id} · {item.revision}</small></span>
|
|
||||||
<StatusBadge status={item.available ? "active" : "locked"} label={item.available ? "Available" : "Restricted"} />
|
<StatusBadge status={item.available ? "active" : "locked"} label={item.available ? "Available" : "Restricted"} />
|
||||||
</button>
|
</SelectionListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{reports.length > 0 && <div className="reporting-list-heading">Semantic reports</div>}
|
{reports.length > 0 && <div className="reporting-list-heading" role="presentation">Semantic reports</div>}
|
||||||
{reports.map((item) =>
|
{reports.map((item) =>
|
||||||
<button
|
<SelectionListItem
|
||||||
type="button"
|
|
||||||
role="listitem"
|
|
||||||
key={item.definition_id}
|
key={item.definition_id}
|
||||||
className={`reporting-report-row${selectedId === item.definition_id ? " is-selected" : ""}`}
|
selected={selectedId === item.definition_id}
|
||||||
onClick={() => { setSelectedId(item.definition_id); setSelectedProviderKey(""); }}>
|
onClick={() => { setSelectedId(item.definition_id); setSelectedProviderKey(""); }}>
|
||||||
<BarChart3 size={17} aria-hidden="true" />
|
<SelectionListItemContent leading={<BarChart3 size={17} />} title={item.name} description={`${item.definition_key} · r${item.revision}`} />
|
||||||
<span><strong>{item.name}</strong><small>{item.definition_key} · r{item.revision}</small></span>
|
|
||||||
<StatusBadge status="active" label="Active" />
|
<StatusBadge status="active" label="Active" />
|
||||||
</button>
|
</SelectionListItem>
|
||||||
)}
|
)}
|
||||||
</div>
|
</SelectionList>
|
||||||
</PageScrollViewport>
|
</PageScrollViewport>
|
||||||
<section className="reporting-result-region">
|
<section className="reporting-result-region">
|
||||||
{selectedProvider ?
|
{selectedProvider ?
|
||||||
@@ -345,7 +345,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
onQueryChange={setQuery}
|
onQueryChange={setQuery}
|
||||||
onParametersChange={setParameters}
|
onParametersChange={setParameters}
|
||||||
/>
|
/>
|
||||||
<ActionToolbar className="reporting-output-toolbar">
|
<ActionToolbar surface="panel-header" className="reporting-output-toolbar">
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
ariaLabel="Report output"
|
ariaLabel="Report output"
|
||||||
value={outputMode}
|
value={outputMode}
|
||||||
@@ -367,12 +367,12 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
}
|
}
|
||||||
</ActionToolbar>
|
</ActionToolbar>
|
||||||
<div className="reporting-output">
|
<div className="reporting-output">
|
||||||
{!execution && <div className="reporting-empty">Run the report or select a previous execution.</div>}
|
{!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} />}
|
||||||
{execution && outputMode === "table" && <ReportTable execution={execution} onDrill={execution.query.mode === "detail" ? undefined : drill} />}
|
{execution && outputMode === "table" && <ReportTable execution={execution} onDrill={execution.query.mode === "detail" ? undefined : drill} />}
|
||||||
</div>
|
</div>
|
||||||
</> :
|
</> :
|
||||||
<div className="reporting-empty">Select a report.</div>
|
<StatePanel size="fill" title="Reports" description="Select a report." />
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
<PageScrollViewport className="reporting-inspector">
|
<PageScrollViewport className="reporting-inspector">
|
||||||
@@ -398,7 +398,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
/>}
|
/>}
|
||||||
</PageScrollViewport>
|
</PageScrollViewport>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</WorkspaceFrame>
|
||||||
<SaveViewDialog
|
<SaveViewDialog
|
||||||
open={saveDialogOpen}
|
open={saveDialogOpen}
|
||||||
onClose={() => setSaveDialogOpen(false)}
|
onClose={() => setSaveDialogOpen(false)}
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
.reporting-page,
|
.reporting-page {
|
||||||
.reporting-shell {
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-shell {
|
.reporting-result-header {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: var(--surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-toolbar,
|
|
||||||
.reporting-result-header,
|
|
||||||
.reporting-output-toolbar {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -21,21 +12,8 @@
|
|||||||
background: var(--surface-raised);
|
background: var(--surface-raised);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-toolbar {
|
|
||||||
min-height: 56px;
|
|
||||||
padding: 9px 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-search {
|
.reporting-search {
|
||||||
display: flex;
|
flex: 1 1 440px;
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
width: min(440px, 46vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-search input {
|
|
||||||
min-width: 140px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-count {
|
.reporting-count {
|
||||||
@@ -66,7 +44,6 @@
|
|||||||
border-left: 1px solid var(--border);
|
border-left: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-report-list,
|
|
||||||
.reporting-inspector-content section {
|
.reporting-inspector-content section {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
@@ -84,7 +61,6 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-report-row,
|
|
||||||
.reporting-inspector-content section > button {
|
.reporting-inspector-content section > button {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -97,45 +73,15 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-report-row {
|
|
||||||
grid-template-columns: 22px minmax(0, 1fr) auto;
|
|
||||||
gap: 8px;
|
|
||||||
min-height: 58px;
|
|
||||||
padding: 8px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-report-row:last-child,
|
|
||||||
.reporting-inspector-content section > button:last-child {
|
.reporting-inspector-content section > button:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-report-row:hover,
|
|
||||||
.reporting-report-row:focus-visible,
|
|
||||||
.reporting-report-row.is-selected,
|
|
||||||
.reporting-inspector-content section > button:hover,
|
.reporting-inspector-content section > button:hover,
|
||||||
.reporting-inspector-content section > button.is-selected {
|
.reporting-inspector-content section > button.is-selected {
|
||||||
background: var(--hover-bg);
|
background: var(--hover-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-report-row.is-selected {
|
|
||||||
box-shadow: inset 3px 0 0 var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-report-row > span:nth-child(2) {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-report-row strong,
|
|
||||||
.reporting-report-row small {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-report-row small,
|
|
||||||
.reporting-inspector-content small,
|
.reporting-inspector-content small,
|
||||||
.reporting-inspector-content p {
|
.reporting-inspector-content p {
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
@@ -251,11 +197,6 @@
|
|||||||
min-width: min(320px, 35vw);
|
min-width: min(320px, 35vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-output-toolbar {
|
|
||||||
min-height: 48px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-output-toolbar > span {
|
.reporting-output-toolbar > span {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
@@ -288,12 +229,6 @@
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-empty {
|
|
||||||
padding: 38px 12px;
|
|
||||||
color: var(--text-soft);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reporting-inspector-content {
|
.reporting-inspector-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -632,13 +567,8 @@
|
|||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-result-header,
|
.reporting-result-header {
|
||||||
.reporting-toolbar {
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-search {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user