Dashboard UI and forms
Цей контент ще не доступний вашою мовою.
The components and helpers that @overvis/hmi-workspace-ui offers dashboards, and the form
scaffold that Setup and dashboards share. For the steps to use them, see
Build a form that saves through the API,
Write parameter values from a dashboard, and
Develop a dashboard locally.
Form scaffold
Section titled “Form scaffold”The package root @overvis/hmi-workspace-ui exports the form scaffold: useAppForm, FormRoot,
mutationFormValidators, focusFirstInvalidField, field helpers, mapTrpcToFormErrors, and
apiErrorMessage. Setup and the host apps build their API forms on it.
const form = useAppForm({ defaultValues: {/* … */}, validators: mutationFormValidators({ schema: someSharedFormSchema, // optional submit: async (value) => { await someMutation.mutateAsync(/* … */); return null; }, // Optional; the default is mapTrpcToFormErrors. // mapError: (error, value) => mapTrpcToFormErrors(error, value, { fieldMap }), }), onSubmitInvalid: focusFirstInvalidField, onSubmit: () => { /* runs after a successful submit */ },});
return ( <form.AppForm> <form.FormRoot> {/* form.AppField → field.TextField */} <form.SubmitButton label="…" pendingLabel="…" /> </form.FormRoot> </form.AppForm>);useAppFormwraps@tanstack/react-formwith the field components andSubmitButton.FormRootalways mountsFormErrorBanner. It blurs the focused control before submit, so Enter includes values that commit on blur.mutationFormValidatorsvalidates the submitted value against the TypeBoxschema, then callssubmit, and maps a thrown API error to{ fields?, form? }withmapError.onSubmitruns only aftersubmitsucceeds.mapTrpcToFormErrorsputsinput_invalidissues and a domain error’sdetails.fieldon the matching field (first path segment or leaf), and anything else on the banner.fieldMapmaps API input names to form field names when they differ.mapErrorruns again whenever the message is read;onErrorruns once per failure.apiErrorMessage(error)is the message for API error codeX: the catalog messageerror_X.focusFirstInvalidFieldfocuses the first invalid field after a failed validation.- Setup form values are
Static<>of the shared form schemas, built from the API input schemas. The shared…FormToInputfunctions, such asendpointCreateFormToInput,parameterFormToInput, andreportPresetFormToInput, turn submitted values into the API input or into{ ok: false, field, problem }.formInputProblemErrors(result, message)turns that problem into a field error.
UI primitives
Section titled “UI primitives”@overvis/hmi-workspace-ui/ui exports shadcn-style primitives (Tailwind 4) and cn. The live-data
components come from the package root. Icons come from lucide-react. The primitives follow shadcn
accessibility patterns, and the workspace shell around them is mobile-first. Which packages a
dashboard can import is listed in the host contract.
| Export area | Examples |
|---|---|
| Actions | Button, ButtonGroup, Toggle, ToggleGroup, Command, ContextMenu |
| Inputs | Input, InputGroup, InputOTP, Combobox, RadioGroup, Calendar, DateTimePicker, Questionnaire |
| Overlays | Dialog, AlertDialog, Sheet, Drawer, Popover, HoverCard, DropdownMenu, Tooltip |
| Navigation | Breadcrumb, NavigationMenu, Menubar, Sidebar, Pagination |
| Layout | Card, Accordion, AspectRatio, Carousel, Resizable, ScrollArea, Separator, Tabs |
| Feedback | NoticeCard, Alert, Badge, Empty, Marker, Skeleton, Spinner, Sonner toasts |
| Data and messages | Table, composed DataTable, Chart, Sparkline, GaugeBar, Gauge, Item, Attachment, Message, MessageScroller |
| Charts | ChartContainer, ChartTooltip, ChartLegend, BarChart, LineChart, AreaChart, ComposedChart, Bar, Line, XAxis |
| Live data | ParameterLiveControls, ParameterValueDisplay, ParameterBooleanBadge, ParameterDiagnostics, useParameterValueHistory |
| Icons | lucide-react components imported directly by the dashboard |
NoticeCard is the problem and status card of the host and the shared components. severity is
error, warning, or info. It takes a short title, a plain description, technical facts in
details, and one primaryAction or onRetry (with retrying) ahead of secondaryActions.
size="compact" fits panels and narrow sections. Alert is the callout of printable documents.
The workspace-ui reference lists every prop.
Button with variant="outline" uses an opaque theme background and matching text color, so it
remains readable over graphics and images. Disabled buttons block activation without passing
pointer clicks through to the content behind them.
Sparkline fits the timestamps it receives by default. With a window of kind: "live",
sampleCount, sampleIntervalMs, and gapThresholdMs, it scrolls with its own clock. With
kind: "fixed" and to, the caller controls the clock. The component keeps the latest reading in
each slot and disconnects the line when the sampling gap exceeds the threshold. range keeps the
vertical scale fixed, and referenceValue draws a horizontal setpoint or limit line.
GaugeBar draws a reading on a horizontal bar whose rounded ends keep their shape at any width.
Positions are in percent of the width: bands such as zones, a fill from the start, ticks for
limits with labels above, and a marker for one reading or the span of several, with a label
below. The bar is hidden from assistive technologies and carries no text alternative.
Gauge shows one live value on a half-circle from 0 to max, with an optional marker tick such
as the period’s peak. gaugeScaleMax picks max. It takes texts already formatted for the locale.
CounterBars from the main entry charts a counter’s increase per hour or day of a period.
Charts
Section titled “Charts”@overvis/hmi-workspace-ui/ui provides the shadcn chart parts and these Recharts parts with their
prop types: BarChart, LineChart, AreaChart, ComposedChart, Bar, Line, Area, XAxis,
YAxis, CartesianGrid, ReferenceLine, and Rectangle. Their props follow the recharts
version pinned by @overvis/hmi-workspace-ui. The host does not serve recharts to dashboards, so
a direct recharts import puts a separate copy of Recharts into the dashboard bundle.
import { Bar, BarChart, CartesianGrid, ChartContainer, ChartTooltip, ChartTooltipContent, XAxis, type ChartConfig,} from "@overvis/hmi-workspace-ui/ui";
const config = { consumed: { label: "Consumed, kWh", color: "var(--chart-1)" },} satisfies ChartConfig;
export function EnergyChart({ rows }: { rows: { month: string; consumed: number }[] }) { return ( <ChartContainer config={config} className="max-h-72"> <BarChart data={rows} accessibilityLayer> <CartesianGrid vertical={false} /> <XAxis dataKey="month" tickLine={false} axisLine={false} /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="consumed" fill="var(--color-consumed)" radius={4} /> </BarChart> </ChartContainer> );}ChartContainer turns each config key into a --color-<key> variable for the chart’s marks and
supplies the tooltip and legend labels. A Bar shape function that renders Rectangle with the
per-bar props styles individual bars. The document components and
ParameterTrendChart cover common charts without composing Recharts parts.
Host chrome (ThemeProvider, LanguageSwitcher, ModeToggle, BrandLink) belongs to the
./host entry and is not available to dashboards.
Styling
Section titled “Styling”Dashboards are styled with Tailwind classes in their source files. hmi build compiles the classes
the dashboard uses into its index.css and scopes them to the dashboard. Classes that appear only
in test modules (*.test.*, *.spec.*, or files under __tests__/) are left out. The host
stylesheet already defines the classes of the shared components, so one class can have a rule in
both stylesheets. Cascade layers decide between them:
| Dashboard class | Layer |
|---|---|
No variant: grid-cols-2, w-full, text-muted-foreground | Below the host utilities |
Variant: hover:, data-[state=on]:, sm:, @min-[30rem]:, … | Above the host utilities |
Variant that workspace-ui also uses, such as hover:bg-muted | The host’s rule for the class |
A plain class and a variant of the same property therefore work as in any Tailwind project:
grid-cols-2 @min-[30rem]:grid-cols-3, w-full sm:w-auto, and
text-muted-foreground hover:text-foreground switch at the breakpoint or state. The same holds for
classes passed to a shared component through className. Shared components keep their own hover,
focus, pressed, and invalid styles when the dashboard uses the same classes. Important classes
(p-0! sm:p-4!) all stay in the lower layer: !important reverses the layer order, so they keep
Tailwind’s order among themselves and still beat the host’s.
A layer outranks source order and specificity. Two cases can still resolve differently than Tailwind orders them, depending on which of the classes workspace-ui uses:
- A shorthand and its longhand on one element, such as
p-4 px-2.py-4 px-2, which sets each side once, has no such conflict. - Two variants for the same property that are active together, such as a
hover:and adata-[state=on]:background on a hovered, pressed item. Exclusive variants, such asnot-data-[state=on]:hover:bg-accent data-[state=on]:bg-primary, have no such conflict.
Dialogs, sheets, drawers, and menus render outside the dashboard, so the dashboard’s own classes do
not reach them. DASHBOARD_SCOPE_PROPS (from @overvis/hmi-shared/dashboard) spread on the
overlay’s content, such as <DropdownMenuContent {...DASHBOARD_SCOPE_PROPS}>, apply the
dashboard’s classes to that element and everything inside it.
Write confirmations
Section titled “Write confirmations”ParameterWriteReviewDialog sends the reviewed changes in one writeValues request and returns
the keys of the failed changes. disabled turns off the confirmation while the changes can no
longer be written, such as after write access or current device values are lost while it is open; a
write already sent completes.
ParameterWriteSequenceDialog writes ordered steps, one at a time, and stops at the first step
that is not acknowledged. Sequences on one page run one after another. DeviceCommandButton wraps
it for a command button. Their disabled option blocks starting the sequence; one already running
finishes. DeviceSettingsSection applies the same rules to a section of device settings and writes
ordered steps, such as an unlock before the changes, when given a write plan; see
device settings. Its validate holds the rules between
settings, such as a minimum above a maximum: the messages show on the rows once something is edited
and block the confirmation. Device dashboards share generic editor and command copy (Apply, no
changes, no permission, invalid number, range, write failed, send command) through the
device_preset_* keys of the host catalog; see
device dashboard components.
The step list shows each parameter name and value; an integer value with a label shows the label.
secret adds a typed value, such as a device password: parameterId is the parameter it is
written to, label and optional description name the field, maxLength limits it, and
validate returns an error message or null. The dialog shows a masked field and writes the typed
text to parameterId in a step before the others, masked in the list. With optional, an empty
field skips that step without calling validate. initialValue prefills the field, and onSent
receives the text when the sequence starts (empty when the step was skipped). stepLabel names the
step where steps are listed by name. The secret is written with writeOnly, so it is never read
back, and the dialog drops the text when it closes.
steps can also be a function. It is called when the dialog opens, for the list, and again when
the sequence starts, so a value such as the current time is the one written.
Write validation failures carry reasons such as write_value_below_min. The runtime codes and
reasons are listed in
Live value procedures.
Write controls
Section titled “Write controls”ParameterControlButton sends a configured command or setpoint, ParameterValueEditor edits a
value or a date-time, and ParameterWriteSequenceDialog writes an ordered group of steps. They use
the host’s workspace and live-value providers. They take translated messages from the dashboard.
disabled shows a control without letting it write, such as in a preview or edit mode.
The dialogs check permissions and parameter writability before they write. Errors found before the
first write can be corrected and retried; once a write starts, another attempt needs the dialog
opened again. The dialog stays open until the sequence finishes or stops, then names the step that
failed and the device’s error when one was reported. onSettled receives the same result, so a
form can mark the fields of the failed step. A step is one write, or { items } sent together in
one request. secretParameterIds masks the values of parameters such as passwords in the list of
steps. Leaving the page stops the steps not yet sent but cannot undo a write already sent.
The value editor uses the same typed-value preparation and boolean or enum choices as Setup. It submits the selected wire value even when translated option labels are identical. Setup’s inline editor also supports numeric and raw-hex display modes and pasted ISO timestamps; strict date-time dialogs accept the displayed local format.
executeParameterWriteSequence from @overvis/hmi-shared/parameter accepts steps (API write
items, or { items } written in one request) and an injected write function. It awaits
acknowledgements and never retries. It checks no permissions and asks for no confirmation. Its
result reports acknowledged, or the 1-based step that stopped with rejected (and the first
reported error), unknown (no acknowledgement, so the step may have been applied), or cancelled.
rejected and a returned unknown carry the step’s per-item results. appDateTimeToISOString
converts strict YYYY-MM-DD HH:mm:ss input in the browser time zone to the wire timestamp.
evaluateParameterCondition returns null for unavailable, stale, diagnostic, or nonnumeric
readings. equalConfigurationValue compares JSON configuration using canonical JSON, ignoring
object key order and omitted undefined properties. ParameterDeadline displays a date parameter,
marks known overdue values, and opens the confirmed date-time editor. PumpStatusIcon is a compact
decorative pump pictogram for use beside a textual status.
Configuration and binding helpers
Section titled “Configuration and binding helpers”DashboardConfigurationRequired is the standard missing-binding screen for dashboards that start
without parameter ids. It lists the missing keys with an example configuration and registers no
live demand; it offers Open dashboard settings only to members who can open Setup. onRetry of
ParameterDiagnostics is optional. @overvis/hmi-workspace-ui/dashboard-bindings finds what it
lists without loading the dashboard kit. Binding groups are TypeBox objects of parameter ids:
missingDashboardBindingGroups(groups, config.parameters) names the fields each group lacks,
exampleDashboardBindings(groups) fills every field with a numbered placeholder id for the
screen’s example configuration, and dashboardBindingFields(group) lists a group’s fields in
declaration order. missingDashboardBindings(requirements) checks groups whose required fields
vary, such as by device model.
useDashboardConfigurationMutation fetches the stored dashboard, validates its configuration
against the supplied TypeBox schema, applies a typed transform, preserves dashboard metadata, and
refreshes the dashboard queries after the update. The update is based on the fetched dashboard: if
the dashboard changed before it is saved, it fails with setup_conflict or
dashboard_configuration_conflict instead of overwriting that change.
DashboardConfigurationRenameDialog is the shared rename form for labels stored in that
configuration. ParameterPickerField picks a parameter for a UUID field on useAppForm, and
ParameterPicker does the same for a value outside a form. The field shows the parameter’s name,
mnemonic, branch path, and value with units, read once; it opens a search over the parameter tree
that matches names in every language, mnemonics, branch paths, and addresses. Both store the
parameter UUID, or an empty string when allowEmpty lets the user clear it. preferred lists
matching parameters first among search results.
Quick reports
Section titled “Quick reports”useDashboardQuickReport(workspaceId) creates a temporary history report, opens it, and reports
skipped parameters or a failure in a toast. useQuickReport creates the temporary report, reports
skipped parameters and failures through caller-supplied localized callbacks, and passes the
resulting report preset ID to caller-owned navigation. Counters among the parameters (energy, heat,
water, gas) passed as counterParameterIds show consumption per time bucket instead of averages.
cadence: "1d" shows daily buckets over the last 31 days instead of hourly ones over the last day.
SVG diagrams
Section titled “SVG diagrams”SvgDiagram loads a vector drawing from an ordinary .svg file and updates the elements marked
with data-svg-target values. An asset imported through
new URL("./assets/diagram.svg", import.meta.url).href is copied beside the bundle by the
dashboard build. The asset must resolve on the same origin as the dashboard host.
SvgDiagram loads and sanitizes the asset, scopes its internal IDs per mounted instance, applies
prepared text, state, and action bindings, and fits the asset into the provided container. It does
not add pan, zoom, or other viewport interaction, and it does not know parameter UUIDs: bindings
carry display strings and state classes that the dashboard prepares. Missing targets are shown as
an authoring diagnostic while the rest of the diagram remains usable.
The sanitizer removes scripts, foreignObject, event attributes, external links, CSS imports, and
external CSS URLs. Fragment references in attributes, inline styles, and embedded <style> blocks
are rewritten to the instance-scoped IDs, so gradients, masks, clip paths, markers, and repeated
diagram instances can share one source asset.
A text binding replaces the children of the targeted <text> or <tspan>. Class, visibility,
accessible-label, and action bindings leave the target’s children intact. Every action target needs
an ariaLabel; SvgDiagram adds button semantics and Enter and Space activation. Classes supplied
by bindings override matching declarations from the asset’s embedded stylesheet, so the SVG can
define fallback appearance for live targets.
SvgDiagram defaults to min-h-80. className overrides that minimum; for example,
aspect-video min-h-0 keeps a 16:9 diagram compact on phones while it fills the desktop column.
DashboardLocationMap from @overvis/hmi-workspace-ui shows named locations on a Google Map. It
handles browser credentials, clustering, bounds fitting, and marker-anchored popups. It takes
locations with stable IDs, coordinates, marker artwork, popup headers, and content, and translated
empty and unavailable messages. Site states, navigation, and popup layouts come from the dashboard.
Updating a location’s coordinates under the same ID refreshes both clustering and map bounds. An
empty location list shows the empty message even when Maps is unavailable. The
fleet and site blocks FleetOverview and
FleetMap combine it with filters and status.
A location has an optional tone: critical, warning, or normal (the default). A cluster
marker shows its member count and takes the color of its worst member: destructive for critical,
amber for warning, and primary otherwise. clusterLabel(count, tone) names cluster markers for
screen readers; without it the name is the count. A location’s optional title names its marker
instead of name, for example “Station 3 – Alarm”, so the map does not rely on color alone.
DashboardLocationPicker places one position in a setup form: clicking the map or dragging the
marker calls onChange with coordinates rounded to six decimals. A changed value moves the
marker and pans the map when it is out of view. Optional context sites appear as muted,
non-interactive markers. It takes a translated label for the map region and an unavailable
message.
When Maps credentials or the SDK fail, including a key that Google rejects for the current site,
the map and the picker show the unavailable message; the rest of the dashboard keeps working. A
translated retryLabel adds a button that requests credentials again and remounts the map.
useMapsBrowserConfig loads the browser key and map ID, useMapsReverseGeocode returns popup
addresses, and readLatLng reads click and marker positions. Maps load in the cloud app and in the
local host at http://localhost:8000; http://127.0.0.1:8000 and LAN IP origins do not load the
map. @vis.gl/react-google-maps is not in the host import map, so a map dashboard bundles it. CLI
workspace scaffolds include the Maps renderer, @googlemaps/markerclusterer, and Google Maps
types, and pull refreshes these managed dependencies for existing workspaces.
The public map template shows a Google Map of named sites. Its sites live in configuration.
Each site has a UUID id, a name (1–60), and position as static { lat, lng } or a geo
parameterId, optional alarm and running parameter ids, and up to eight values. The
configuration also has optional center, zoom (1–20), allowAdd, and allowDelete (both
default false). Until a successful live value exists, the pin is loading. After that it is
unreachable if every bound parameter has a settled error, alarm if the alarm flag is true,
operating if running is true, and standby otherwise. Live GNSS pins stay independent markers.
Only static { lat, lng } sites cluster.
Editable graphics
Section titled “Editable graphics”DashboardGraphics shows diagrams that users can correct in the browser. It shares a versioned
JSON format with assistant authoring and supports parameter bindings, reusable symbols, and
workspace images. See editable dashboard graphics for geometry, storage, and
conflict-aware saving.
Related reading
Section titled “Related reading”@overvis/hmi-workspace-ui:LocalWorkspaceApp,DashboardPage, forms- Dashboard host contract: props, exports, imports, and builds
- Live data in dashboards
- Dashboards internationalization: locale prop and dates
- Build a form that saves through the API
- Develop a dashboard locally
- Embedding reports in dashboards