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

Dashboard host contract

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

A dashboard is a React module that the host loads into the workspace page, next to the sidebar. This page lists what the module must export and what the host gives it. For the steps to write one, see Develop a dashboard locally. The catalog fields and API procedures are in Dashboard procedures.

The default export is a React component that receives DashboardRootProps from @overvis/hmi-shared/dashboard:

type DashboardTheme = "light" | "dark";
type AppLocale = "en" | "pl" | "de" | "uk" | "ru";
interface DashboardRootProps {
configuration: Record<string, unknown>;
theme: DashboardTheme;
locale: AppLocale;
workspaceId: string;
dashboardId: string;
}
  • configuration is the dashboard’s configuration, validated against dashboardConfigType before mount. parseDashboardConfiguration(type, configuration) narrows it to the static type.
  • theme is "light" or "dark", with the system setting already resolved. Host tokens such as text-foreground and bg-primary follow the theme without the prop.
  • locale is the active UI language when the module’s supportedLocales lists it, and English otherwise; see Dashboards internationalization.
  • workspaceId is the open workspace, for workspace procedures such as parameter.listInventory. Entity procedures such as report.preset.get take only { id }.
  • dashboardId is the dashboard’s own ID, for settings and diagnostic links.

The props change while the dashboard stays mounted, for example when the operator switches theme or language.

ExportRequiredContent
defaultYesReact component that accepts DashboardRootProps
dashboardConfigTypeYesTypeBox type of the configuration, defined in config.ts
supportedLocalesNoLiteral array of the languages the copy implements, with English
pagesNoFixed pages in tab order, each a path and a name (a string or a per-language map)

supportedLocales and pages are declared in manifest.ts and re-exported from the entry. A module without supportedLocales is treated as supporting en, pl, uk, and ru. pages uses the DashboardPage type of @overvis/hmi-shared/dashboard, with the home page / first. The build records both, so tools that list dashboards show the same tab names.

The dashboard’s settings in Setup validate the configuration against dashboardConfigType and block saving while it does not match, naming each problem by its path. Configuration schema in the settings shows the schema the build writes from it, so describe each key in the type.

A module with a config.ts and an entry:

config.ts
import Type from "typebox";
export const dashboardConfigType = Type.Object(
{ title: Type.Optional(Type.String()) },
{ additionalProperties: false },
);
import {
parseDashboardConfiguration,
type DashboardRootProps,
} from "@overvis/hmi-shared/dashboard";
import { dashboardConfigType } from "./config.ts";
export { dashboardConfigType } from "./config.ts";
export default function MyDashboard(props: DashboardRootProps) {
const configuration = parseDashboardConfiguration(dashboardConfigType, props.configuration);
return <div data-theme={props.theme}>{configuration.title ?? "Dashboard"}</div>;
}

The Empty template starts a dashboard with these files:

src/dashboards/<dashboard-uuid>/
index.tsx # entry: re-exports the root component, dashboardConfigType, supportedLocales
dashboard.tsx # default-exported root component
config.ts # dashboardConfigType (required)
manifest.ts # supportedLocales and optional pages
config.schema.json # written by the build

The entry is the first existing file of index.tsx, index.ts, index.jsx, and index.js; it can define the default export itself or re-export it. manifest.ts holds literals and type imports only, because the build evaluates it on its own.

Dashboards are styled with Tailwind classes in the dashboard folder’s .ts, .tsx, .js, and .jsx files; any class works, including arbitrary values such as h-[80svh]. Classes in shared folders such as src/lib are not picked up. The build writes the dashboard’s stylesheet itself and takes no .css files or CSS imports. The host provides the workspace-ui base styles; see styling.

Dashboards share one React, one copy of @overvis/hmi-workspace-ui, and one TypeBox with the host. The host supplies these packages at run time, so the dashboard’s build does not include them:

ImportContent
react, react-dom, and their jsx-runtime and clientReact
@overvis/hmi-shared and its subpathsProps and other types, locales, formatters, schemas
@overvis/hmi-workspace-uiLive values, forms, report widgets, graphics, maps, trpc
@overvis/hmi-workspace-ui/uishadcn UI primitives, charts, cn, toasts
@overvis/hmi-workspace-ui/i18nuseTranslation and useLocale for the host’s UI messages
typeboxConfiguration types

These are bundled with the dashboard:

  • lucide-react icons, tree-shaken to the icons the dashboard uses;
  • other packages installed in the workspace project, such as @vis.gl/react-google-maps for maps;
  • the dashboard’s own files under src/dashboards/<id>/. A lazy import() of its own modules works with React.lazy and Suspense.

@overvis/hmi-workspace-ui/host is the application shell and is not available to dashboards. The contents of each package are in @overvis/hmi-shared and @overvis/hmi-workspace-ui.

hmi build, which hmi push runs, writes per dashboard an ES module bundle with source maps, a utilities-only stylesheet scoped to the dashboard, config.schema.json (the JSON Schema of dashboardConfigType), and the hmi-dashboard.json manifest with the contract version, supportedLocales, and pages. See the CLI reference.

The build fails, and nothing is published, when:

  • config.ts or the dashboardConfigType export is missing;
  • manifest.ts declares invalid supportedLocales or pages (duplicate paths, or not / first), or the entry does not re-export them directly from ./manifest.ts;
  • type checking finds undefined names or a default component whose props do not match, reported with file and line;
  • a local import or a workspace symbol cannot be resolved.

A successful build does not check rendering or live device behavior. Builds read no environment variables or .env files, because published bundles reach every viewer of the workspace; pass settings through the configuration. Scripts can run the same build with @overvis/hmi-cli/dashboard-build.

When the host contract changes incompatibly, a dashboard built with older tools shows Dashboard needs a rebuild instead of loading. Update the HMI packages and push again.

The cloud app runs a dashboard’s published build, from the moment the dashboard is created from its template and after each successful push. The local host runs its source and reloads it as you edit.

Before mounting, the host checks that the module has a default React component and the dashboardConfigType export, and validates the configuration against it. While loading, the page shows a skeleton. A failed load shows an error notice with Retry; for a configuration that fails validation, editors, admins, and owners also get a link to the dashboard’s settings. The component is then mounted inside the live-value provider with the props above.

A browser keeps a loaded dashboard for the rest of its session. Publishing through the assistant and reverting to a checkpoint reload open dashboards. After a push from the CLI or a Setup edit, a browser that already loaded the dashboard keeps the old build until the page is refreshed.

A dashboard can own any number of pages under /workspace/<workspaceId>/d/<dashboardId>/.... The cloud and local hosts accept the same deep links. The dashboard module, configuration, and live-value provider stay mounted when its path, query, or anchor changes. All pages share one dashboard’s access, source, configuration, and publication. The host adds no tabs, menus, breadcrumbs, or page registry.

useDashboardNavigation and DashboardLink come from @overvis/hmi-workspace-ui. The hook returns location, href(target), and navigate(target, { replace?, keepScroll? }). location.path is relative to the dashboard root and starts with /; the root is /. location.search is the query string, including ? when present, with every value kept as a string. location.hash has no leading #. Path and anchor stay percent-encoded as in the URL, so a location passes back to href or navigate unchanged.

const navigation = useDashboardNavigation();
const filters = new URLSearchParams(navigation.location.search);
const stationLocation = {
path: `/stations/${stationId}`,
search: filters.toString(),
hash: `pump-${pumpId}`,
};
<DashboardLink to={stationLocation}>Open station</DashboardLink>;
// For an action that is not a link:
await navigation.navigate(stationLocation);

Targets accept optional path, search, and hash. An omitted path means /; an omitted query and anchor are cleared. Query and anchor accept values with or without their leading punctuation. Paths cannot contain dot segments, including encoded ones, or leave the dashboard; malformed percent escapes, backslashes, and control characters are rejected. href returns a copyable URL. DashboardLink is a real anchor that keeps modified clicks and new tabs; replace adds no history entry. Navigation respects unsaved-change prompts.

A new path opens at the top of the page, as in a browser, and a query change on the same path keeps the scroll position. A link with an anchor scrolls to that anchor instead. keepScroll keeps the position on a new path too, for example for a detail overlay at its own path over the page the user was reading; Back and Forward between the two keep it as well.

<DashboardLink to={{ path: `/pumps/${pumpId}`, search: navigation.location.search }} keepScroll>
{pumpName}
</DashboardLink>

dashboardPageAt(paths, location.path) returns the page whose path matches, ignoring a trailing slash. DashboardPageTabs links the pages as tabs with the current one marked, and DashboardPageNotFound links an unknown path back to a chosen page. The host does not scroll to an anchor whose element appears only after data loads; useAnchorScroll does.

PathPage
/workspace/$workspaceIdOpens the first enabled dashboard
…/d/$dashboardIdThe dashboard
…/setup/dashboardsCatalog list; a name opens /d/$dashboardId
…/setup/dashboards/newCreate dialog over the list
…/setup/dashboards/new-from-reportCreate dialog that picks a permanent report
…/setup/dashboards/$dashboardIdSettings dialog over the list

The sidebar lists enabled dashboards in the catalog order. The local host uses the same paths. Workspace pages describes each page.

In a pulled workspace, pnpm dev runs the local host, LocalWorkspaceApp, with the workspace API key that the pull created. It has the same workspace pages as the cloud app, without the assistant panel. A dashboard loads from its source and updates as you edit while the page stays mounted. A dashboard whose source is not pulled yet runs its published build with a warning. The props of LocalWorkspaceApp are in @overvis/hmi-workspace-ui.

In the cloud app, the assistant can point at an element of a running dashboard. It reads data-context-* attributes, such as data-context-widget="pump-start" and data-context-label="Start", from the picked element and its ancestors. Put them on the widgets operators ask about. data-overvis-dashboard is reserved for the host.

When the assistant creates or edits dashboard code, it checks its unpublished draft in a preview that shows the saved configuration and live equipment readings. The monitor icon (Watch assistant preview) in the assistant chat header shows that preview; the viewer is read-only, and Stop in the chat interrupts the assistant and keeps the draft.

In the preview, the assistant can navigate, open dialogs, fill forms, and try different screen sizes. Device writes and other lasting changes are refused there; readings come from real equipment. The preview loads Google Maps; other internet-hosted maps, images, and fonts are missing there, so build dashboards from the installed packages and workspace assets. If the preview stays unavailable, the assistant keeps the draft unpublished, and the chat offers a retry or publishing without the visual check. Build and render errors always block publishing.

A dashboard runs with the workspace access of the signed-in user, or of the workspace API key in a local host. Writes go through the live-value procedures, which check permissions and writability on the server.