Each entity output now includes a url field:
- deviceProfile: /profiles/deviceProfiles/{id}
- device: /entities/devices/{id}
- gateway: /entities/gateways (generic, no ID)
- dashboard: /dashboards/{id}
- ruleChain: /ruleChains/{id}
Available in templates as ${device.url}, ${dashboard.url}, etc.
- Add GATEWAY step: creates gateway device, outputs dockerComposeUrl
- Add GATEWAY_CONNECTOR step: saves connector config as shared attributes,
appends to active_connectors array on the gateway
- Add optional serverAttributes/sharedAttributes fields to all entity steps
(DEVICE, GATEWAY, DEVICE_PROFILE, DASHBOARD, RULE_CHAIN)
- Attribute files are read from ZIP, variable-resolved, and saved via
AttributeService after entity creation
The admin settings endpoint requires SYS_ADMIN authority but the device
install wizard runs as TENANT_ADMIN.
Fix: add getConnectivityInfo(baseUrl) to DeviceConnectivityService which
resolves host/port with baseUrl fallback (reusing existing getHost/getPort
logic). Expose via new TENANT_ADMIN endpoint GET /api/iot-hub/connectivity.
Remove AdminService dependency from the dialog component.
Fetch connectivity settings from admin API on wizard init and make
transport variables available in all templates and instructions:
${http.host}, ${mqtt.port}, ${coap.host}, etc.
Resolution priority: form values > transport vars > entity outputs.
Drop ${step1.id}, ${step2.name} etc. positional variable resolution.
Only named entity outputs (${device.id}, ${dashboard.name}) are supported.
Updated spec to reflect the simplified resolution priority.
- Extract filter panel content into ng-template for reuse
- Desktop sidebar (tb-iot-hub-filters-desktop): hidden on lt-lg
- Filter toggle button: shown on lt-lg with active filter count
- mat-drawer (right-side overlay): 60% width on lt-lg, 100% on lt-md
- Drawer has header with close button, active filter chips, and filter body
- mat-drawer-container wraps content area for proper drawer integration
- Add activeFilterCount getter and filterDrawerOpened property
- New addItem(itemType, options?) method opens add item dialog via service
- Options: optional itemSubType and entityId
- Refactored dashboards, rule chains, devices, widget types, calculated fields
to use iotHubActions.addItem() instead of opening dialog directly
- Removed direct TbIotHubAddItemDialogComponent imports from all table configs
- All table configs now inject IotHubActionsService
- New iot-hub-components.models.ts with shared ITEM_TYPE_TO_ENTITY_TYPE
mapping and resolveEntityDetailsUrl function
- Remove duplicated ITEM_TYPE_TO_ENTITY_TYPE from install and update dialogs
- Remove duplicated resolveEntityDetailsUrl from install and update dialogs
- Both dialogs now use shared function from models file
- Make installDevice public in IotHubActionsService
- Add item dialog: for DEVICE type, invoke iotHubActions.installDevice
instead of standard install flow
- Add "Add from IoT Hub" action to devices table (tenant scope)
- Update IotHubModule to import IotHubComponentsModule instead of HomeComponentsModule
- Update CLAUDE.md with IotHubActionsService and IotHubComponentsModule docs
- New IotHubActionsService with openItemDetail, installItem, updateItem,
deleteItem methods returning Observables for dialog results
- Service handles all dialog opening, device install flow (fetch zip + dialog),
and delete confirmation + API call
- Registered as provider in IotHubComponentsModule
- Refactored browse component: removed all dialog imports, uses service
- Refactored home component: removed dialog imports, uses service
- Refactored search component: removed dialog imports, uses service
- Removed ~150 lines of duplicated dialog code across 3 components
- Move iot-hub-install-dialog to home/components/iot-hub/
- Move iot-hub-update-dialog to home/components/iot-hub/
- Move iot-hub-delete-dialog to home/components/iot-hub/ (with new separate HTML/SCSS)
- Move device-install-dialog to home/components/iot-hub/device-install-dialog/
- Register all in HomeComponentsModule (declarations + exports)
- Remove from IotHubModule declarations
- Update all imports across home/browse/search/installed/detail/widget-select
- Remove iotHubApiService from IotHubItemDetailDialogData, inject in constructor
- Remove iotHubApiService from IotHubInstallDialogData, inject in constructor
- Remove iotHubApiService from IotHubUpdateDialogData, inject in constructor
- Remove iotHubApiService from DeviceInstallDialogData, inject in constructor
- Update all callers (home, browse, search, installed items, widget select,
item detail dialog) to remove iotHubApiService from dialog data
- Fix device install dialog data (item + zipData) after bulk removal
- IotHubApiService injected in dialog constructor, removed from data interface
- Add optional entityId to dialog data, passed to installItemVersion for CF
- Add optional itemSubType to dialog data and fixedSubType to browse component
- fixedSubType: applies subtype filter, hides subtype filter section/chips,
excluded from active filters and clear all, hides card subtype badge
- Add "Add from IoT Hub" to calculated fields table with entityId
- Rule chains: pass CORE/EDGE subType based on ruleChainScope, fix navigation
- Show alert dialog on install failure with error details
- Remove IotHubApiService from all dialog data callers
- Add 'add' mode to item card: shows "Add" button, emits addClick
- Add 'add' mode and addItem output to browse component
- Browse passes mode to cards and detail dialog, propagates add results
- New TbIotHubAddItemDialogComponent: fullscreen dialog with embedded browse
in add mode, installs item via API and returns descriptor
- Add "Add from IoT Hub" menu item to dashboards table config
- On add: installs dashboard, navigates to newly created dashboard
- Add HomeComponentsModule import to IotHubModule
- Move iot-hub-browse.component to home/components/iot-hub/
- Move iot-hub-item-card.component to home/components/iot-hub/
- Register both in HomeComponentsModule (declarations + exports)
- Remove from IotHubModule declarations
- Update relative imports to use @home/pages/iot-hub/ for dialog dependencies
- Add All/Installed toggle in IoT Hub mode header
- Installed mode fetches published versions for installed widgets with in-memory filtering
- Filter panel with Type, Category, Use Case sections via tb-popover
- Filter badge count on filter button
- Installed badge (check + label) on widget cards in All mode
- Widget type badge from dataDescriptor on IoT Hub widget cards
- Filters apply to both All (server-side) and Installed (in-memory) modes
- Load installed widgets on entering IoT Hub mode
- Skip detail dialog for already-installed widgets, emit directly
- Move item detail dialog to home/components/iot-hub for shared access
- Register in HomeComponentsModule (declarations + exports)
- Add 'add' mode to item detail dialog showing "Add" button instead of install
- Add 'iotHub' mode to dashboard-widget-select with IoT Hub widget grid
- IoT Hub cards show author + install count footer
- Clicking card opens detail dialog in 'add' mode
- Add installs widget via IoT Hub API then emits widgetSelected with full type info
- Add "IoT Hub" option to mode toggle in dashboard page header
- Hide filter/deprecated controls in IoT Hub mode
- Hero keywords clickable to navigate to items pages
- Use subscriptSizing="dynamic" on hero search and search page inputs
- Replace native select with mat-select for items-per-page in search page
- Propagate search text to type pages via ?search= query param
- Remove back button negative margin
- New TbIotHubSearchComponent at /iot-hub/search with ?search= query param
- Results grouped by item type with section headers linking to type pages
- 5-column grid for widgets/dashboards, 3-column for calculated fields/rule chains
- Search input with clear button, sort dropdown, pagination with page numbers
- Empty state with search_off icon when no results found
- Reuses item card component with install/update/delete dialog handling
- Search popup using mat-autocomplete with results grouped by item type
- Debounced search with distinctUntilChanged, page size 10, most installed sort
- Loading spinner shown immediately on first focus while fetching
- No results state with search_off icon and contextual message
- Clear button in search input to reset text
- "See all results" footer navigates to /iot-hub/search page
- Search button performs same navigation as "See all results"
- Popup styled to match design: 424px max-height, 8px radius, layered shadow
- Update table type chips, update column, and filter panel to match design
- Add afterClosed handling to viewItemDetails and viewUpdateDetails dialogs
- Reload table data when items are updated or deleted from detail dialog
- Back link + "Installed Items" title + "Check for updates" primary button
- Filter button (stroked primary) with dropdown checkbox panel for item types
- Filter chips (mat-chip-set) with "Clear all" button when active
- Search field matching browse page design (320px, rounded-8, outlined)
- Mat-table with columns: Name, Type (colored icon+text), Version,
Installed date, Updates (up-to-date/available+update btn), Actions
- Actions: info, open-in-new, remove (mdi:trash-can-outline) icon buttons
- Updates column: green "Up to date" or "vX.X.X Available" + Update stroked warn
- Item type filter passed as List<String> to API
- Use TbIotHubDeleteDialogComponent for remove confirmation
- Item card: installed chip (non-clickable pill, green) + separate remove icon
button (mdi:trash-can-outline) for both same-version and update-available states
- Detail dialog: info section matches design exactly (badge, links, actions layout),
solution template links on separate row, remove button with warn outline matching
label text color, matButtonIcon + tb-mat-24 on remove icon
- Rename all Delete labels/keys to Remove across locale and components
- Solution template remove dialog: specific description about multiple entities
- getInstalledItems API: change itemType to List<String> itemTypes through full stack
(controller, service, DAO, repository JPQL uses IN clause, UI accepts string|string[])
* Add device-package.models.ts with install wizard types
Defines ConnectivityType, InstallStepType, ENTITY_STEP_TYPES,
stepTypeAliasMap, DevicePackageInfo, DeviceInstallStep, form field
interfaces, and EntityStepProgress types for the device install wizard.
* Add device install wizard dialog component for IoT Hub
Multi-step dialog that provisions TB entities from a device package ZIP:
- Connectivity type selector, instruction/markdown views, dynamic forms
- Entity creation with progress tracking (device profile, device, dashboard, rule chain)
- Variable resolution across steps (form values, entity outputs, positional refs)
* fix(iot-hub): fix device install dialog template and translations
- Use correct tb-markdown inputs ([data] and [usePlainMarkdown])
- Fix translation key format (hyphens, not dots)
- Remove unused ConnectivityType import and resolveImageUrl method
- Auto-advance to first step when single connectivity type
- Use inline data URIs for helpImage directly
* feat(iot-hub): wire device install dialog into all install entry points
Branch DEVICE item type to fetch ZIP and open TbDeviceInstallDialogComponent
instead of the simple install dialog. Applied to:
- Item detail dialog (install button)
- Browse component (card install button)
- Home component (card install button)
* fix(iot-hub): fix build errors in device install dialog
- Fix tb-markdown input: [markdownText] → [data] in done view
- Fix JSZip entry type annotation for strict TS compilation
* fix(iot-hub): reuse existing device profiles and add missing retry translation
- Find device profile by name before creating; reuse if exists
- Add action.retry translation key (was showing raw key)
* feat(iot-hub): add find-or-create for rule chains in device install
Reuse existing rule chains by name (same pattern as device profiles).
Shared entities (device profile, rule chain) are looked up before creation.
Per-install entities (device, dashboard) are always created fresh.
* feat(iot-hub): refactor device install dialog to use mat-stepper
- Replace single-panel view switching with mat-horizontal-stepper
- Group consecutive entity steps into a single "Provisioning" stepper step
- Each SHOW_INSTRUCTION and SHOW_FORM is its own stepper step
- Add 2s minimum display time per entity creation step
- Connectivity selector shown before stepper (not a stepper step)
- Progress step auto-advances to next step after completion
- Stepper shows completed checkmarks for finished steps
* fix(iot-hub): fix stepper navigation by tracking step completion
Linear mat-stepper requires steps to be marked completed before
advancing. Added completed flag to WizardStep, set it when user
clicks Next or when entity steps finish.
* feat(iot-hub): add device package install tracking with entity cleanup
Backend:
- Expand DeviceInstalledItemDescriptor with createdEntityIds and dashboardId
- Add POST /api/iot-hub/device/register endpoint to save installed item
- Implement reverse-order entity deletion for DEVICE uninstall
- Update DEVICE case in updateItemVersion to clean old entities
Frontend:
- Expand DeviceInstalledItemDescriptor TS model
- Add registerDeviceInstall API method
- Wire wizard to collect entity IDs and register after completion
- Enable dashboard navigation for installed device packages
* fix(iot-hub): add type discriminator to device register request body
Jackson requires the 'type' field for @JsonTypeInfo polymorphic
deserialization of DeviceInstalledItemDescriptor.
* feat(iot-hub): show entity type in provisioning progress rows
Each progress row now displays "Device Profile — ESP32 Dev Kit" instead
of just "ESP32 Dev Kit", making it clear what type of entity is being
provisioned.
* fix(iot-hub): suppress global error toast and add Back button on provisioning error
- Add ignoreErrors: true to all entity creation API calls so errors
are only shown in the dialog, not as global toast notifications
- Add Back button on provisioning error to navigate back to the last
form step, allowing users to fix parameters (e.g. device name)
- Make form steps editable so stepper allows backward navigation
- Reset progress step state when going back so it re-runs with
updated form values
* fix(iot-hub): resolve ExpressionChangedAfterItHasBeenCheckedError in device install dialog
Inject ChangeDetectorRef and call detectChanges() after async state
mutations (entity step status changes, stepper auto-advance, ngOnInit
completion). The NG0100 error occurred because async operations
(setTimeout, HTTP calls) mutated component state outside Angular's
change detection cycle.
* fix(iot-hub): fix device register endpoint JSON deserialization
Accept JsonNode and use JacksonUtil.treeToValue() instead of direct
@RequestBody DeviceInstalledItemDescriptor. The @JsonTypeInfo on the
parent interface caused Spring's message converter to fail when
deserializing the concrete class directly.
* fix(iot-hub): fix dashboardId serialization in device register request
DashboardId implements EntityId which uses EntityIdDeserializer requiring
both entityType and id fields. Frontend was sending {"id":"..."} without
entityType, causing "Missing entityType or id!" 400 error.
Fix: include entityType: "DASHBOARD" in the dashboardId payload.
Add deserialization unit test to verify the exact frontend payload format.
- Create TbIotHubItemsPageComponent with hero section (title, description,
illustration, installed items button, contribute link) per item type
- Add separate routes: /iot-hub/widgets, /iot-hub/dashboards,
/iot-hub/solution-templates, /iot-hub/calculated-fields,
/iot-hub/rule-chains, /iot-hub/devices
- Remove old /iot-hub/browse route, update all navigation links
- Browse component: add @Input activeType and hideTabs for embedded use
- Home component: navigateToBrowse now routes to type-specific pages
- Add item type page description translations
- Two-column layout: left sidebar filters (280px, own scroll) + right card grid
- Filters use mat-expansion-panel accordions with checkbox lists
- Type, Category, Use Case filter sections with expand/collapse
- Top bar: results count + search field, sort button with mat-menu
- Active filter chips row with clear all button
- 4-column card grid (responsive: 3 at 1200px, 2 at 900px)
- Card subtype label: @Input showSubtype, 12px Medium uppercase above name
- Tabs use plural item type translations
- Remove old header, filter dropdowns, and footer sections
- Add getInstalledItemsCount endpoint through full stack (controller → DAO)
with optional itemType filter
- Add getInstalledItemsCount to UI IotHubApiService
- Home page: show "Installed Items: N" stroked button at top-right when
count > 0, white bg, primary border, card shadow; navigates to installed items
- Count updates on install/delete via API re-fetch and local decrement
- Move IoT Hub menu item right after Dashboards in sidebar
- Create TbIotHubDeleteDialogComponent with warn-colored "Delete anyway" button
- Extract install dialog template and styles to separate HTML/SCSS files
- Install dialog: simplified confirm/success layout matching design, "Close"
label when only button shown on success
- Item card: installed label is now a clickable button emitting deleteClick
- Detail dialog: 64px colored icon header for CF/RC types, description-only
layout (no image) for CF/RC, delete button (stroked warn) in footer when
installed, closes with 'deleted' result after confirmed deletion
- Home component: handle install/delete dialog results by reloading installed
items, pass installedItem to detail dialog