feat: widget registry and "add widget" picker (#21) #35

Merged
pitfriedrich merged 1 commits from ai/issue-21-widget-registry-picker into main 2026-07-28 18:36:02 +00:00
Collaborator

Closes #21

Problem

DashboardView.addWidget() only ever appended a placeholder card ("Frei platzierbare Karte."). Combined with the closable widgets from #14 this was lossy: a widget closed via its X could not be brought back without a full page reload.

Change

  • widgets/WidgetDefinition — record: stable type id, i18n titleKey, default width/height, Supplier<Component> factory.
  • widgets/WidgetRegistry@Service holding the definitions in registration order (revenue-trend line, revenue-month bar, revenue-region pie). The chart construction moves here out of DashboardView; factories pull their numbers from ChartDataService (#20) and resolve translation keys through the chart component, so they follow the current UI locale without the registry being a Component.
  • DashboardView — "Widget hinzufügen" opens a picker Dialog listing the registered types instead of appending a placeholder. The initial widgets are built from the registry as well (keeping their existing ids and placement, so persisted layouts still match).
  • Ids: added widgets get <type>-<n> from a counter that only ever grows and is shared across types, so a closed widget's id is never handed to a new widget — otherwise the new one would inherit the closed one's saved position.
  • i18n: new gridstack.pickerTitle in all three bundles; the now-unused gridstack.widget / gridstack.widgetText placeholder keys are removed. Card titles reuse the existing card.* keys.

Tests

./mvnw test — 31 tests green (incl. the Playwright e2e ones).

New/updated in DashboardViewTest: picking a type adds a widget that renders a real chart; a closed widget can be re-added and gets a fresh id; adding the same type twice yields distinct ids; the picker lists every registered type. WidgetRegistryTest covers ordering, require() on an unknown type, and re-registration (the factories need a Vaadin UI for translations, so they are exercised through the view test).

Note on scope

The diff is ~320 added lines, above the ~200-line guideline in CLAUDE.md. About a third is tests and Javadoc, and the registry + picker are hard to land separately without leaving the picker referring to a registry nothing uses. Flagging it rather than splitting after the fact — say the word and I'll cut it into "registry only" and "picker" PRs.

🤖 Generated with Claude Code

https://claude.ai/code/session_0124BiJikbhbiEsNfJxdWM69

Closes #21 ## Problem `DashboardView.addWidget()` only ever appended a placeholder card ("Frei platzierbare Karte."). Combined with the closable widgets from #14 this was lossy: a widget closed via its X could not be brought back without a full page reload. ## Change - **`widgets/WidgetDefinition`** — record: stable `type` id, i18n `titleKey`, default `width`/`height`, `Supplier<Component>` factory. - **`widgets/WidgetRegistry`** — `@Service` holding the definitions in registration order (`revenue-trend` line, `revenue-month` bar, `revenue-region` pie). The chart construction moves here out of `DashboardView`; factories pull their numbers from `ChartDataService` (#20) and resolve translation keys through the chart component, so they follow the current UI locale without the registry being a `Component`. - **`DashboardView`** — "Widget hinzufügen" opens a picker `Dialog` listing the registered types instead of appending a placeholder. The initial widgets are built from the registry as well (keeping their existing ids and placement, so persisted layouts still match). - **Ids**: added widgets get `<type>-<n>` from a counter that only ever grows and is shared across types, so a closed widget's id is never handed to a new widget — otherwise the new one would inherit the closed one's saved position. - **i18n**: new `gridstack.pickerTitle` in all three bundles; the now-unused `gridstack.widget` / `gridstack.widgetText` placeholder keys are removed. Card titles reuse the existing `card.*` keys. ## Tests `./mvnw test` — 31 tests green (incl. the Playwright e2e ones). New/updated in `DashboardViewTest`: picking a type adds a widget that renders a real chart; a closed widget can be re-added and gets a fresh id; adding the same type twice yields distinct ids; the picker lists every registered type. `WidgetRegistryTest` covers ordering, `require()` on an unknown type, and re-registration (the factories need a Vaadin UI for translations, so they are exercised through the view test). ## Note on scope The diff is ~320 added lines, above the ~200-line guideline in `CLAUDE.md`. About a third is tests and Javadoc, and the registry + picker are hard to land separately without leaving the picker referring to a registry nothing uses. Flagging it rather than splitting after the fact — say the word and I'll cut it into "registry only" and "picker" PRs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0124BiJikbhbiEsNfJxdWM69
claude-bot added 1 commit 2026-07-28 18:33:06 +00:00
fix: add a widget registry and "add widget" picker (#21)
CI / build-and-test (pull_request) Successful in 2m25s
5fe159a604
"Widget hinzufügen" appended an empty placeholder card, so a widget closed
via its X was gone until a page reload. Widget types now live in a
WidgetRegistry (WidgetDefinition: type id, title key, default size,
content factory); the toolbar button opens a picker dialog over the
registry, and the dashboard builds its initial widgets from it too.

Chart construction moves from DashboardView into the registry factories,
which resolve translation keys through the chart component so they follow
the UI locale. Added widgets get an id of "<type>-<n>" from a
monotonically growing counter, so a closed widget's id is never handed to
a new one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0124BiJikbhbiEsNfJxdWM69
pitfriedrich merged commit cdbd61aefb into main 2026-07-28 18:36:02 +00:00
pitfriedrich deleted branch ai/issue-21-widget-registry-picker 2026-07-28 18:36:03 +00:00
Sign in to join this conversation.