Skip to content
Updated Sep 22, 2026 by Pablo Coufal · Owner: analysisactivefeatureconfluence-migration Edit on GitHub

Chart Types, Dashboard Tiles & Custom Views ​

Business Context ​

This feature governs how configured chart content is displayed, once the content itself has been configured (segment, granularity, filters — the Chart Engine). It provides a curated set of predefined chart types the user picks from for a given piece of configured content, the ability to toggle individual data series on or off within a chart, and the ability to combine several chart tiles on one dashboard or report screen. It is a presentation layer built on top of the Chart Engine, not a replacement for it.

The word "chart type" covers two different choices, and the screen makes both. A view — total values, specific values, values by usage, year comparison, interval data, indoor environment quality — is chosen first and decides what is asked of the engine. A rendering — column, line, stacked column, pie, heatmap, bubble — is chosen after the content is configured and decides how the answer is drawn. The registry in FR1 holds renderings; views are a short list of their own.

No existing view or rendering is removed; heatmap and bubble are additive. Composing several charts together on one dashboard or report screen must be possible.

Business-Level Definition ​

Provide a curated, extensible chart-type picker and a lightweight save/reuse mechanism for configured charts, so that a user who has already defined what to look at can independently choose how to see it, and can place several such views together on one screen.

Requirements Definition ​

  • A curated, extensible list of predefined chart types (existing types carried forward, plus heatmap and bubble, with room to add more later without a redesign)
  • A chart-type picker shown after chart content has been configured, not before
  • Per-chart series toggle (show/hide individual data series without re-querying)
  • An extended, chart-type-aware config filter and an extended list of selectable periods
  • The ability to define and save a custom chart (content configuration + chosen chart type + series-visibility state) for reuse
  • The ability to place more than one chart, of possibly different types, on a single dashboard or report screen
  • One charting library behind the registry — Highcharts, reusing the licence the previous system runs on — with the registry keeping every consumer independent of it

Acceptance Criteria ​

  • A user picks the view before configuring content, and the screen offers only the settings that view supports; an unsupported setting is visible but disabled and says why
  • A user who has configured chart content sees a rendering picker listing only the renderings valid for that content, and can switch between them without reconfiguring the content
  • Selecting a chart type does not require leaving or resetting the content-configuration step
  • A user can toggle any single data series on/off within a rendered chart, and the toggle state is part of what a saved custom chart remembers
  • A user can save a chart (content config + chart type + series-visibility state) under a name and reopen it later with the same configuration
  • A dashboard or report can hold more than one chart tile at once, each independently configured and independently typed
  • No consumer imports the charting library directly: a rendering is reached through the registry, so replacing the library touches the renderers and nothing else
  • Existing chart types are not removed or behaviorally changed by this feature
  • Selecting a segment, granularity, and one or more attribute filters (FR4) produces a request matching the Chart Engine contract exactly
  • An IČO filter input (FR4) is validated and normalized (zero-padded to 8 digits) before the request is sent; invalid input is rejected before calling the API
  • Leaving an attribute filter empty (FR4) omits that parameter entirely from the request
  • Multiple values can be selected for any single attribute filter (FR4) in one request
  • The asset cascade offers a group type, a group, a building, a gauge type and a gauge; a selection is shown as removable chips and is cleared with the mouse, not with a modifier key
  • Changing the unit re-requests the data and renders what the engine returns; nothing is converted in the browser
  • The table under a chart is built from the same response as the chart — same values, same order, one row per selected item plus a total row — and hiding a series dims its row without changing any number
  • Choosing a 15-minute granularity with a longer period renders the window the engine served and says which window that is

N/A — not available in the source material.

Technical Context ​

User Stories / Use Cases ​

  • As a user who has just configured a chart's content, I want to pick from a list of chart types appropriate to that data, so I can view it the way that's most useful to me.
  • As a user viewing a multi-series chart, I want to hide series I'm not interested in right now, without re-running the query.
  • As a user, I want to save a chart I've configured so I don't have to rebuild it next time.
  • As a user assembling a dashboard or report, I want to place several charts of different types next to each other, so I can see multiple views of my data at once.

UI/UX Design ​

The approved layout is the "variant D" prototype: views across the top, settings in a left panel (metric, media, unit, period, granularity, source mode, ordering), and the asset scope as a five-column cascade — group type → group → building → gauge type → gauge — summarised as removable chips with a count of the series the query will return. The chart, its legend and the value table sit in the working area below.

A setting the selected view does not support stays visible but disabled, with the reason on it, so the screen teaches what the view can answer instead of hiding the rest. The same rule covers a metric whose data does not exist yet: it is shown as unavailable with its dependency named, never silently absent.

Accessibility: every control is reachable and operable from the keyboard, including the cascade and the legend; the chart carries a text alternative and the value table is that alternative in full — a row per period read left to right is the same story the chart tells, which is why it is part of the screen rather than an optional panel; series are distinguished by more than colour; contrast is checked in both themes.

The prototype is an information-architecture study, not a rendering study: legend, tooltip, zoom, export and accessibility are settled during implementation against the design system.

Functional Requirements ​

FR1 — Rendering registry. A centrally defined, type-keyed registry lists every available rendering together with which content shapes each one accepts.

  • Column, line, stacked column and pie carried forward from the previous system; heatmap and bubble added as new entries
  • The registry is the only place that knows which library draws a rendering; no view, tile or report block imports the library itself
  • Mirrors the tile-type-registry pattern used by Dashboard Management and the block-type registry used by Report Builder — one type→schema source shared by every consumer, not duplicated per view
  • This registry's relationship to the Dashboard tile-type registry was an open question — resolved, see FR5

FR2 — Two levels: view first, rendering after the content.

  • The view is chosen first, in the top bar. It decides which query is built and which settings are offered; switching view is a new question and re-runs the query.
  • The rendering is chosen once content (view, scope, medium, period, granularity, unit) has been configured, and only renderings compatible with that content shape per the FR1 registry are offered.
  • Switching rendering re-draws the same response — no new request, no reconfiguration, no change to any value.

FR3 — Series toggle, value table and saved charts.

  • Clicking a legend entry hides or shows that series, in the browser only — no new request.
  • The value table under the chart is a component of this feature drawn from the same response as the chart. It reads like a spreadsheet of the period: one row per bucket, one column per series, and a total row at the bottom carrying each series' period total. Nothing in it is added up in the browser — every figure, including the total row, comes from the response.
  • The column header is composed of levels, one line each, and the levels are the ones the user chose in the filter — the header is a reading of the selection, not a fixed five-line template. Only a campus picked in the cascade and nothing below it gives a header of two lines: the campus and the value type. Narrowing the cascade one column further — group, building, gauge type, gauge — adds that column's line, and choosing more than one medium adds the medium line; a level the user never narrowed never appears. The value type (the unit the figures are in, and later the quantity, once a column can be money rather than energy) is always the last line, because it is what the number actually is.
  • Adjacent columns that share a level are spanned under one heading, so five gauges of two buildings read as two buildings. Levels keep the cascade's order — group type → group → building → gauge type → gauge → medium → value type — so the header reads the same way the filter was filled in.
  • A series the engine returns that the header cannot place — a subject at a level the user did not select — keeps its own column under the levels that do apply; the table never invents a level to hold it.
  • The selection total is stated beneath the table rather than as a column, because it is not the sum of anything on screen: where the selection holds a gauge and its sub-meter, the total row adds to more than the selection actually consumed. The line beneath says the figure, the difference and which subject is already counted inside another — two numbers that disagree are explained where they disagree, not left to be noticed.
  • The states the engine reports are shown as themselves: a bucket with nothing behind it as a dash, a partly covered bucket marked with its coverage, an unavailable value named with its reason, a provisional value marked. Where several media are on the chart, the period total also carries each medium's share. It is not the charting library's built-in data table, which would carry only the visible series and format its cells outside our control.
  • A hidden series is dimmed in the table; its numbers and the total stay as the engine returned them. Visibility is remembered by the series' stable id, so hiding a building's gas does not hide its electricity.
  • A user can save a configured chart — view, content configuration, rendering, series visibility and the period as a preset rather than fixed dates — under a name and reopen it later.

FR4 — Extended configuration and composition.

  • Config filter and selectable-period list follow the Chart Engine's finalized contract — granularity levels (year/month/day/hour/15-minute) and attribute-based filters (building ownership, management, delegated management, campus, UČEH code, PENB class) per Chart Engine FR2 and FR7. This feature does not define its own filter/period list — it surfaces the Chart Engine's contract in the picker UI.
    • Enumerable/bounded attributes (campus, PENB class) use a list-style control, selected from existing values.
    • Free-text/code attributes with no fixed enumeration (owner, manager, delegated-manager IČO) use a text input with exact-match validation (zero-padded 8 digits) before the request is sent.
    • Multi-value selection is the default for all of the above, consistent with today's report/graph filter convention.
    • An unset attribute filter sends no corresponding query parameter — no restriction, not "select nothing."
    • UČEH-code filtering is excluded until it's exposed upstream (see Chart Engine FR7).
    • The scope is a five-column cascade — group type → group → building → gauge type → gauge — where each column offers only what the columns to its left allow, and the result is summarised as removable chips. The group type carries sector, campus node and the whole client. Owner, manager and PENB class are group types only once they are selectable as lists rather than typed codes; until then they remain attribute filters in the left panel, where Chart Engine FR7 puts them.
  • Period presets replace the legacy list: last 5 years, last 3 years, last 12 months, calendar year, heating season (1 September – 31 May) and a custom range, each resolved to explicit dates that stay visible. The calendar year is the default; "everything available" is not carried over, because on a long history it is the slowest query on the screen. Every preset is available at every granularity — the engine serves the period it is asked for — and a custom range that does not fall on bucket boundaries renders the clipped edge buckets with the boundaries the response states. The screen says how many points a request will draw before it runs: until measurement sets a limit, a wide 15-minute selection is the user's own choice to wait for.
  • Media are chosen as a set, not one at a time: electricity, gas and heat can be read on one chart, because they share a unit and add up to a figure worth reading. Each medium is drawn as its own series with its own colour — a shared unit is the reason they can sit on one axis, not an instruction to merge them into a single column. The figure that spans them lives in the table's total row. Water is a volume, so it is never drawn on that axis — selecting it alongside energy produces a second chart beneath the first, from a second request, with its own unit, its own value table and its own total. The screen never asks the engine to mix the two, and never adds a cubic metre to a kilowatt-hour on its own.
  • Unit is part of the content, not a display toggle: changing it re-requests the series and renders what comes back, so the chart, the table and any total stay in one unit. The offered units are those the selected media share.
  • More than one chart, of independently chosen types, can be placed on a single dashboard or report screen — reuses the composition mechanics from FR5, not invented from scratch

FR5 — Registry ownership.

  • Dashboard Management already has a working, type-keyed tile registry (type → size/aspect ratio → required config keys → renderer), extended via a documented 3-step process. A chart-type picker could be:

    • (a) an extension of that registry — each chart type becomes a new tile type
    • (b) a config-level dimension inside one generic chart tile type.
  • Report Builder maintains its own, separate block-type registry (chart/table/text). A chart-type picker used inside a report's chart block should reuse whichever mechanism is chosen above, rather than introducing a third, independent chart-type list.

  • Decision (2026-09-01) — option (b). One generic chart tile type and one generic chart block type, with the chart kind as a validated config key. The chart-kind vocabulary lives in its own module, which the Dashboard tile registry, the Report Builder block registry and the saved-chart record all reference as a config value — one list, no copies, satisfying FR1's "one type→schema source shared by every consumer". This is not a third, independent chart-type list: it is the only list, and it is shared rather than owned by either registry.

    Why (b) rather than (a):

    • FR2's content-shape compatibility is orthogonal to tile geometry — under (a) it would sit in the tile registry next to aspect ratio, meaningless for every non-chart tile.
    • FR3's saved chart already persists the chosen type as data, so (a) would hold the same user choice twice, in two different shapes.
    • Chart kinds do not differ in the only property the tile registry discriminates on (default size and aspect ratio), so (a) yields entries identical but for a label.
    • An unknown tile type is dropped whole by the dashboard render step, costing the user a pinned panel; an unknown chart kind inside a known chart tile degrades in place.

    The two existing registries (Dashboard tile registry, Report Builder block registry) stay separate — this ratifies a split already implemented rather than reopening it. Full record: docs/architecture/35-chart-type-registry.md.

FR6 — Independence from the charting library and from the engine's readiness.

  • Renderings are reached only through the FR1 registry, so the charting library is an implementation detail of the renderers.
  • The screen is built against the documented Chart Engine contract and reads a mocked route handler until the real endpoint exists; the swap is a single configuration point, as in Dashboard Management and Report Builder.

Internationalization & Localization ​

Every label on the screen — views, settings, period presets, table headers, states — comes from the application's label catalogue, so the Czech and English variants stay in one place. Numbers and dates are formatted for the user's locale (Czech decimal comma and thousands space by default); units are rendered from the code the engine returns, never re-derived. The heating-season preset is a Czech convention, named as such, not a computed season.

Non-Functional Requirements ​

  • The rendering registry must be a single, centrally defined source, not duplicated between the picker, the save/reuse flow, and the render step
  • A rendering must never be inferred implicitly from data shape without the registry's explicit compatibility declaration (FR1)
  • Toggling a series must not trigger a new data query (client-side operation only); changing the view, the scope, the period, the granularity or the unit does
  • No number is computed in the browser: values, totals, ordering and coverage come from the engine and are rendered as received
  • Series colour follows the medium, taken from the design system, with shades distinguishing several series of the same medium, so a chart carrying three media stays readable; contrast is checked in both themes
  • Transactional Operations: saving/updating/deleting a custom chart is a single-row write; no multi-step transaction required

Performance ​

  • Switching the rendering or toggling a series never re-requests data; changing the view, scope, period, granularity or unit does, and the screen shows a loading state rather than stale numbers.
  • The series count the query will return is shown before it runs, so a user sees the cost of a wide selection in advance.
  • Chart and table render from one response held once; the table is virtualised when a fine granularity produces many columns.

Transactional Operations ​

Saving, renaming or deleting a saved chart is a single-row write; nothing else on this screen writes.

  • Chart Engine answers every query this screen makes; nothing is computed here.
  • Dashboard Management pins a saved chart as a tile through the generic chart tile type (FR5) — the dashboard needs no new type.
  • Report Builder places the same saved chart as a report block through the shared rendering list.
  • Benchmarking stores a group median as a saved chart rather than inventing its own container.
  • User Preferences supplies the default unit the screen opens with.

Diagrams & Models ​

flowchart TD
    V[View chosen in the top bar] --> F[Left panel: metric, medium, unit, period, granularity, ordering]
    V --> C[Asset cascade: group type, group, building, gauge type, gauge]
    F --> Q[Query to Chart Engine]
    C --> Q
    Q --> R[(One response: series, totals, coverage, unit)]
    R --> G[Rendering from the registry]
    R --> T[Value table]
    G -. legend toggle .-> T
    R --> S[Saved chart: view + content + rendering + visibility]

API Analysis ​

GET    /v1/charts/{chartId}    Get a saved chart's configuration
POST   /v1/charts              Save a new named chart
PUT    /v1/charts/{chartId}    Replace a saved chart's configuration
DELETE /v1/charts/{chartId}    Delete a saved chart

Exact shape is provisional pending finalization of the entity chosen for a saved chart (see Domain Model below).

Domain Model (ER diagram) & Data Attribute Table ​

The screen itself stores nothing; it reads Chart Engine and renders the response. One record belongs to it: the saved chart, which holds the view, the content configuration, the rendering and the series visibility as one configuration value — the same list of renderings the dashboard tile and the report block reference (FR5), so a saved chart can be pinned or placed without being copied.

erDiagram
    savedChart ||--o{ dashboardBlock : "pinned as"
    savedChart ||--o{ reportBlock : "placed as"
    savedChart }o--|| user : "owned by"
  • dashboardBlock — the tile that renders a saved chart
  • reportBlock — the report block that renders it
  • savedChart — its Data Attribute Table is written in the Entity DAT catalog by the slice that builds it; until then the shape it stores is the one under Data below.

Data ​

The screen holds no data of its own; what it renders is one response of Chart Engine. A saved chart is a configuration record of the shape:

json
{
  "name": "Objekty s největší spotřebou",
  "view": "totalValues",
  "chartKind": "column",
  "query": {
    "metric": "consumption",
    "medium": ["electricity", "gas"],
    "scopeType": "sector",
    "scopeId": ["…s1"],
    "breakdown": "building",
    "granularity": "month",
    "period": { "preset": "calendarYear" },
    "unit": "kWh",
    "sortKey": "total",
    "sortDirection": "desc"
  },
  "hiddenSeries": []
}

The period is stored as a preset, not as dates, so the chart reopens on the current year rather than on the year it was saved in.

Test Data ​

The E2E tenant seed named in the project overlay carries the fixtures this screen renders — search it for the chart-engine block described in that feature's Test Data (CHART-E1, CHART-B1, CHART-S1, CHART-C1). The screen adds two records of its own to that seed: a saved chart owned by the test user and one shared inside the client, both named CHART-SAVED-*.

Logging ​

  • .info: custom chart created, updated, or deleted (chart id, chosen type, actor)
  • .debug: chart-type compatibility check result for a given content shape
  • .warn: attempt to select a chart type incompatible with the current content shape

Monitoring ​

N/A — not covered in source material beyond the logging entries above.

Caching ​

N/A — not covered in source material.

Backward Compatibility and Migration ​

No data migration needed — chart-type preference is new interaction, not historical or reportable data.

The screen displays the tenant's own operational measurements and stores only a saved chart's configuration — a selection and its display settings, no measured values and no personal data beyond the owner of the saved chart. A chart shared inside the client carries the sharer's selection, not their access: whoever opens it sees only the subjects they may see. Nothing here is exported outside the tenant; export formats and their retention belong to the reports feature.

Cybersecurity Considerations ​

  • The screen shows only what the engine returns for the signed-in user; it never widens a selection on its own and never caches another user's response.
  • A saved chart stores a selection, not data: reopening it re-queries, so a user who loses access to a building stops seeing its series and the chart says what is missing.
  • Sharing a saved chart inside the client is governed by the sharing permission, following the pattern the overview filters already use.

Risk Assessment ​

Business Risks ​

RiskBusiness impactMitigation
A number formatted or re-summed in the browserChart, table and export disagree in front of a customerEverything is rendered as the engine returned it
The screen answers fewer questions than the previous systemUsers keep the old application open beside the new oneViews cover the previous view types; a view not yet available is visible and says so
A saved chart reopening on stale datesA manager reviews last year believing it is this yearThe period is saved as a preset
A wide selection at a fine granularityAn unreadable chart read as "the product is slow"The series count is shown before the query runs; the served window is labelled

Technical Risks ​

RiskConsequenceMitigation
View and rendering implemented as one listHalf the questions become inexpressible and the registry has to be rebuiltTwo levels, separated in FR1 and FR2
The charting library imported directly by views, tiles or report blocksReplacing it touches every consumerRenderings are reached only through the registry (FR6)
The library's built-in data table adoptedHidden series vanish from the table and cell formats escape the design systemOwn table component over the same response (FR3)
Mock and real responses driftingThe screen works until the day the backend landsOne shared schema validates both; a single switch point

Auditing, Reporting & Measurement ​

Saved charts are created, updated and deleted under the logging entries below, with the actor and the chosen rendering. Everything else on the screen is a read; what is worth measuring is which views, renderings and period presets are actually used, which is what tells whether the next slice should add a rendering or a view.


Originally migrated from Confluence page "Chart Types, Dashboard Tiles & Custom Views" (id 689963012). Revised against the EM2 report screens, the EM3 codebase, the approved layout and the decisions recorded for the first consumption-chart slice. The saved-chart entity remains open and is decided with the slice that builds it.