Adopt shared WebUI layout primitives
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
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 {
|
import { ContentGrid, ActionToolbar,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
@@ -174,7 +174,7 @@ export function ProviderReportWorkspace({ settings, auth, report }: {
|
|||||||
</div>
|
</div>
|
||||||
{execution ?
|
{execution ?
|
||||||
<>
|
<>
|
||||||
<div className="reporting-output-toolbar">
|
<ActionToolbar 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")} />
|
||||||
@@ -182,7 +182,7 @@ export function ProviderReportWorkspace({ settings, auth, report }: {
|
|||||||
{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")} />
|
||||||
}
|
}
|
||||||
</div>
|
</ActionToolbar>
|
||||||
<div className="reporting-provider-output">
|
<div className="reporting-provider-output">
|
||||||
<ProviderResult execution={execution} />
|
<ProviderResult execution={execution} />
|
||||||
</div>
|
</div>
|
||||||
@@ -227,11 +227,11 @@ function ProviderResult({ execution }: { execution: ProviderReportExecution }) {
|
|||||||
return (
|
return (
|
||||||
<Card title={group} key={group}>
|
<Card title={group} key={group}>
|
||||||
{metrics.length > 0 &&
|
{metrics.length > 0 &&
|
||||||
<div className="dashboard-grid reporting-provider-metrics">
|
<ContentGrid columns={2} collapseAt="workspace" className="reporting-provider-metrics">
|
||||||
{metrics.map((field) =>
|
{metrics.map((field) =>
|
||||||
<MetricCard key={field.path} label={field.label} value={displayValue(pathValue(execution.result, field.path))} />
|
<MetricCard key={field.path} label={field.label} value={displayValue(pathValue(execution.result, field.path))} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</ContentGrid>
|
||||||
}
|
}
|
||||||
{details.length > 0 &&
|
{details.length > 0 &&
|
||||||
<dl className="detail-list">
|
<dl className="detail-list">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
type FormEvent
|
type FormEvent
|
||||||
} from "react";
|
} from "react";
|
||||||
import {
|
import { FormGrid, ActionToolbar,
|
||||||
Button,
|
Button,
|
||||||
DataGrid,
|
DataGrid,
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -250,7 +250,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
return (
|
return (
|
||||||
<main className="reporting-page">
|
<main className="reporting-page">
|
||||||
<div className="reporting-shell">
|
<div className="reporting-shell">
|
||||||
<div className="reporting-toolbar">
|
<ActionToolbar className="reporting-toolbar">
|
||||||
<form className="reporting-search" onSubmit={submitSearch}>
|
<form className="reporting-search" onSubmit={submitSearch}>
|
||||||
<Search size={17} aria-hidden="true" />
|
<Search size={17} aria-hidden="true" />
|
||||||
<input
|
<input
|
||||||
@@ -271,7 +271,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => void reload()}
|
onClick={() => void reload()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</ActionToolbar>
|
||||||
{error &&
|
{error &&
|
||||||
<DismissibleAlert tone="danger" resetKey={error}>
|
<DismissibleAlert tone="danger" resetKey={error}>
|
||||||
{error}
|
{error}
|
||||||
@@ -345,7 +345,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
onQueryChange={setQuery}
|
onQueryChange={setQuery}
|
||||||
onParametersChange={setParameters}
|
onParametersChange={setParameters}
|
||||||
/>
|
/>
|
||||||
<div className="reporting-output-toolbar">
|
<ActionToolbar className="reporting-output-toolbar">
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
ariaLabel="Report output"
|
ariaLabel="Report output"
|
||||||
value={outputMode}
|
value={outputMode}
|
||||||
@@ -365,7 +365,7 @@ export default function ReportingPage({ settings, auth }: PlatformRouteContext)
|
|||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</ActionToolbar>
|
||||||
<div className="reporting-output">
|
<div className="reporting-output">
|
||||||
{!execution && <div className="reporting-empty">Run the report or select a previous execution.</div>}
|
{!execution && <div className="reporting-empty">Run the report or select a previous execution.</div>}
|
||||||
{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} />}
|
||||||
@@ -757,7 +757,7 @@ function PublishDialog({ open, targets, onClose, onPublish }: {
|
|||||||
</Button>
|
</Button>
|
||||||
</>}>
|
</>}>
|
||||||
{dialogError && <DismissibleAlert tone="danger" resetKey={dialogError}>{dialogError}</DismissibleAlert>}
|
{dialogError && <DismissibleAlert tone="danger" resetKey={dialogError}>{dialogError}</DismissibleAlert>}
|
||||||
<div className="reporting-dialog-grid">
|
<FormGrid columns={2} gap="small" collapseAt="narrow">
|
||||||
<label className="reporting-dialog-field">
|
<label className="reporting-dialog-field">
|
||||||
<span>Target</span>
|
<span>Target</span>
|
||||||
<select value={targetCapability} onChange={(event) => {
|
<select value={targetCapability} onChange={(event) => {
|
||||||
@@ -784,7 +784,7 @@ function PublishDialog({ open, targets, onClose, onPublish }: {
|
|||||||
<label className="reporting-dialog-field"><span>Sender address</span><input type="email" value={fromAddress} onChange={(event) => setFromAddress(event.target.value)} /></label>
|
<label className="reporting-dialog-field"><span>Sender address</span><input type="email" value={fromAddress} onChange={(event) => setFromAddress(event.target.value)} /></label>
|
||||||
<label className="reporting-dialog-field reporting-dialog-span"><span>Subject</span><input value={subject} onChange={(event) => setSubject(event.target.value)} placeholder="Generated from report name" /></label>
|
<label className="reporting-dialog-field reporting-dialog-span"><span>Subject</span><input value={subject} onChange={(event) => setSubject(event.target.value)} placeholder="Generated from report name" /></label>
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</FormGrid>
|
||||||
{target?.reason && <DismissibleAlert tone="warning" dismissible={false}>{target.reason}</DismissibleAlert>}
|
{target?.reason && <DismissibleAlert tone="warning" dismissible={false}>{target.reason}</DismissibleAlert>}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
@@ -936,10 +936,10 @@ function ScheduleDialog({ open, onClose, onSave }: { open: boolean; onClose: ()
|
|||||||
<Button onClick={onClose}>Cancel</Button>
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
<Button variant="primary" disabled={!name.trim() || saving} onClick={() => { setSaving(true); void onSave(name.trim(), Number(interval)).finally(() => setSaving(false)); }}>Schedule</Button>
|
<Button variant="primary" disabled={!name.trim() || saving} onClick={() => { setSaving(true); void onSave(name.trim(), Number(interval)).finally(() => setSaving(false)); }}>Schedule</Button>
|
||||||
</>}>
|
</>}>
|
||||||
<div className="reporting-dialog-grid">
|
<FormGrid columns={2} gap="small" collapseAt="narrow">
|
||||||
<label className="reporting-dialog-field"><span>Name</span><input value={name} onChange={(event) => setName(event.target.value)} autoFocus /></label>
|
<label className="reporting-dialog-field"><span>Name</span><input value={name} onChange={(event) => setName(event.target.value)} autoFocus /></label>
|
||||||
<label className="reporting-dialog-field"><span>Interval</span><select value={interval} onChange={(event) => setIntervalValue(event.target.value)}><option value="3600">Hourly</option><option value="86400">Daily</option><option value="604800">Weekly</option><option value="2592000">Every 30 days</option></select></label>
|
<label className="reporting-dialog-field"><span>Interval</span><select value={interval} onChange={(event) => setIntervalValue(event.target.value)}><option value="3600">Hourly</option><option value="86400">Daily</option><option value="604800">Weekly</option><option value="2592000">Every 30 days</option></select></label>
|
||||||
</div>
|
</FormGrid>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -611,12 +611,6 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-dialog-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
.reporting-workspace {
|
.reporting-workspace {
|
||||||
grid-template-columns: minmax(220px, 28%) minmax(0, 1fr);
|
grid-template-columns: minmax(220px, 28%) minmax(0, 1fr);
|
||||||
@@ -647,7 +641,4 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reporting-dialog-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user