Перейти до вмісту

Write a document dashboard in code

Цей контент ще не доступний вашою мовою.

A document dashboard presents an answer, such as an energy analysis, a commercial proposal, or an installation estimate. This guide writes one as code in a pulled workspace. The components are described in Document components for code dashboards.

If the document needs only text, figures, tables, charts, live values, tiles and inputs, create it with the Document template instead, as data and without code: see Document specification. The reading orders below apply to both.

  1. Create a dashboard with the Empty template and pull the workspace.
  2. Replace dashboard.tsx with the page. Keep config.ts.
  3. In manifest.ts, set supportedLocales to the languages the page’s copy implements, such as ["en"].

Start with the conclusion, then supporting figures, comparisons, recommendations, and assumptions. These orders are starting points:

TaskSuggested composition
Energy analysisMain finding, consumption and cost metrics, supporting chart, recommended actions, estimated benefits, methodology and sources.
Commercial proposalClient need, proposed solution and connection diagram, equipment and scope table, budget, delivery sequence, assumptions and exclusions.
Installation estimateSite conditions and assumptions, quantities and labor, cost breakdown, low/base/high scenarios where justified, uncertainties.
  1. Wrap the page in DashboardDocument.
  2. Open with DocumentLead, and use the conclusion as its title. The host already shows the dashboard’s name.
  3. Put each part in a DocumentSection. Inside DocumentProse, use h3 for sub-headings.
  4. Use DocumentColumns for metrics or figures side by side, and DocumentMetric for headline numbers. Include units or currency in the value.
  5. Keep tables out of prose blocks, so the prose line length does not squeeze numeric columns. Right-align numeric headers and cells with className="text-right", and give each table a caption or an accessible label.
  6. Put important caveats in an Alert, and methodology and sources in a DocumentProse section.
  7. Add a diagram only when it explains a relationship. Put SvgDiagram or dashboard graphics in a DocumentFigure, or use ordinary React elements and Lucide icons for simple relationships. Keep diagram text readable on narrow screens.
  1. Aggregate large datasets before you pass them to DocumentChart; it draws every row.
  2. Keep the number of series small, and combine only series in comparable units. Draw incompatible units in separate charts.
  3. Pass formatValue with units and a sensible precision, and keep axis labels short.
  4. Validate untrusted input against DocumentChartDataType before rendering it.
  5. For irregular stored readings, embed a history report widget instead.
  1. Wrap each control in a Field with a FieldTitle that has an id, and label the control with that id.
  2. Narrow the Slider value with typeof value === "number".
  3. Derive totals, charts, and dependent prose from the same values, so they agree when an input changes.

Scenario controls change only React state. To save configuration or write to equipment, use the shared form scaffold.

  1. Keep the sources, input assumptions, currency, tax basis, and calculation notes on the page.
  2. State the time range, time zone, and material gaps of an analysis. Format timestamps with formatAppDateTime.
  3. Separate verified prices from budgeting allowances, and mark the inputs the user supplied. Keep a missing price unknown or label it as an allowance; do not let it become zero.
  4. When live components sit beside written findings, label the period each section describes, so a current reading is not taken for the evidence behind an earlier conclusion. Use the standard loading, error, and quality indicators in live sections.

This dashboard.tsx uses illustrative allowances; replace them with evidence or clearly stated assumptions. One list feeds the table, chart, and totals, so they agree after the quantity changes. The copy is English; for several languages, follow Localize a dashboard.

import { useId, useState } from "react";
import type { DashboardRootProps } from "@overvis/hmi-shared/dashboard";
import {
DashboardDocument,
DocumentChart,
DocumentColumns,
DocumentFigure,
DocumentLead,
DocumentMetric,
DocumentProse,
DocumentSection,
DocumentTable,
} from "@overvis/hmi-workspace-ui";
import {
Alert,
AlertDescription,
AlertTitle,
Field,
FieldDescription,
FieldTitle,
Slider,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@overvis/hmi-workspace-ui/ui";
export default function Estimate({ locale }: DashboardRootProps) {
const [quantity, setQuantity] = useState(4);
const quantityLabel = useId();
const lines = [
{ label: "Equipment", detail: `${quantity} meters at €300 each`, cost: quantity * 300 },
{ label: "Labor", detail: `${quantity * 2} hours at €60/hour`, cost: quantity * 2 * 60 },
];
const total = lines.reduce((sum, line) => sum + line.cost, 0);
const money = (value: number) =>
new Intl.NumberFormat(locale, {
style: "currency",
currency: "EUR",
maximumFractionDigits: 0,
}).format(value);
return (
<DashboardDocument>
<DocumentLead
title="Budget for equipment and installation together."
context="Preliminary estimate · Illustrative allowances"
>
<p>
The base allowance for {quantity} meters is {money(total)}, excluding tax.
</p>
</DocumentLead>
<Alert variant="warning">
<AlertTitle>Allowances, not supplier quotations</AlertTitle>
<AlertDescription>Confirm prices and site access before ordering.</AlertDescription>
</Alert>
<DocumentColumns>
<DocumentSection title="Quantity">
<Field>
<FieldTitle id={quantityLabel}>Meters: {quantity}</FieldTitle>
<Slider
aria-labelledby={quantityLabel}
min={0}
max={12}
step={1}
value={[quantity]}
onValueChange={(value) =>
setQuantity(typeof value === "number" ? value : (value[0] ?? 0))
}
/>
<FieldDescription>Reloading restores the initial quantity.</FieldDescription>
</Field>
</DocumentSection>
<DocumentMetric
label="Base estimate"
value={money(total)}
detail="Existing cabinet space and two labor hours per meter assumed."
/>
</DocumentColumns>
<DocumentColumns>
<DocumentTable aria-label="Cost breakdown">
<TableHeader>
<TableRow>
<TableHead>Item</TableHead>
<TableHead className="text-right">Cost</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{lines.map((line) => (
<TableRow key={line.label}>
<TableCell>{line.detail}</TableCell>
<TableCell className="text-right">{money(line.cost)}</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell>Total excluding tax</TableCell>
<TableCell className="text-right">{money(total)}</TableCell>
</TableRow>
</TableFooter>
</DocumentTable>
<DocumentFigure caption="Current equipment and labor allowances, excluding tax.">
<DocumentChart
kind="bar"
label="Cost allocation"
formatValue={money}
empty="No cost data available."
data={{
series: [{ key: "cost", label: "Cost" }],
rows: lines.map((line) => ({ label: line.label, values: { cost: line.cost } })),
}}
/>
</DocumentFigure>
</DocumentColumns>
<DocumentSection title="Basis and exclusions">
<DocumentProse>
<p>
Allow €300 per meter and two installation hours at €60/hour. These are assumptions, not
verified supplier quotations.
</p>
<p>
Travel, cabinet alterations, recurring fees, and tax are excluded. Confirm site access,
cable routes, and the shutdown window before ordering.
</p>
</DocumentProse>
</DocumentSection>
</DashboardDocument>
);
}
  • To change a document the assistant made, ask the assistant; follow-up requests normally edit the same dashboard. To keep both versions, ask for a separate dashboard.
  • To edit the sources yourself, pull the workspace, edit dashboard.tsx, and push.