Skip to content

Resource identity system: pills, one-line identity cells, hover summaries and select options - #324

Merged
roncodes merged 38 commits into
release/v0.6.66from
feature/resource-identity-system
Sep 16, 2026
Merged

roncodes merged 38 commits into
release/v0.6.66from
feature/resource-identity-system

Conversation

@roncodes

Copy link
Copy Markdown
Member

Adopts the resource identity system from @fleetbase/ember-ui v0.4.1 across FleetOps: every place a record references another record now shows that resource's pill, one-line identity cell, hover summary or select option.

Depends on fleetbase/ember-ui#175 (v0.4.1) being published. Until the @fleetbase/ember-ui range is bumped this branch builds but the new components resolve to nothing at runtime; the engine's instance-initializer looks up service:resource-registry and does nothing when it is absent, and no ember-ui module is imported. CI on this branch is expected to fail on the build until the range is bumped; the verdict is reported below.

What changed

Descriptors (addon/utils/resource-descriptors/{assets,people,connectivity,maintenance,operations,fuel,sub-records,polymorphic}.js, registered by instance-initializers/register-fleetops-resource-descriptors) — all 67 fleetops-data models: 34 first-class resources with a full descriptor, 16 sub-records with a degraded one (icon tile, dot only with a status, a few facts, opening delegated to the parent or nothing), 13 polymorphic subtypes as aliases of their concrete resource, and the attachable, facilitator and maintenance-subject bases as dispatchers that resolve the concrete descriptor from the record or its *_type. Parts hydrate their fitted asset and work orders their linked schedule on hover only. Fact labels are translated under resource-summary.facts.*.

Wrappers — <key>/pill, <key>/summary, cell/<key>-identity and select-option/<key> for every key, as template-only files over the ember-ui bases. The existing vehicle, driver, trailer, device, equipment, order and inspection-submission pills keep their named arguments; the vehicle tooltip and the order QR body now live in the summaries (the tracking-number descriptor's image is the QR). The identity cells drop their JS and tone maps.

Tables — 56 related-record columns render identity cells (vehicles, drivers, trailers incl. towing vehicle, fleets, fuel reports, fuel transactions, issues, contacts/customers/vendors address, orders, service rates, connectivity events/sensors/telematics, device-table-columns, inspection submissions, maintenances, schedules, work orders, payments, map drawer listings, fuel provider transactions). Rows with only a *_name get buildIdentityStub (utils/identity-cell-resource.js): no UUID in identifier fields, one in-flight load per row. Async relations are read through the local utils/relation-value.js. The four filter/multi-model columns are filter/model-multiple; the 38 filter/model columns pick up option rendering from ember-ui.

Details — every detail field naming a related record renders its pill (vehicle, driver, fuel report, order, trailer, work order, maintenance, schedule, equipment, part, sensor, device, device event, fleet, service rate, inspection submission, warranty, the device vehicle tab, fuel transaction and connectivity event templates). Inside clickable cards (vehicle and trailer cards, inspection history, fleet listings) the compact select option is used instead, since a nested click target is not allowed.

Pickers — all 128 ModelSelects and the 8 record-backed PowerSelects render select-option/<key> with the compact variant in the trigger; the 9 polymorphic pickers resolve it from the chosen model name with a text fallback. Audit output (record pickers without @selectedItemComponent):

addon/templates/settings/notifications.hbs:20 PowerSelectMultiple

That one selects { label, type } notifiable descriptors, not records, so it is left as is. hide-from-trigger / normalize-in-trigger remain only in enum pickers (19 files).

Deleted — the select-option base and select-option/user, the .select-option* and .fleetbase-pill > a[href] CSS, filter/multi-model and its CSS, cell/driver-name, cell/vehicle-name, cell/telematic-provider, and the unused cell/attached-vehicle and cell/telematic-device.

Bugs fixed (one commit each)

  • B1 b0ae9ee driverActions.panel.view ignored promise proxies and loadResource() stubs
  • B2 c645155 the vehicles and trailers vendor columns called a vendorActions.viewVendor that never existed (in the tables commit)
  • B3 c645155 the orders driver/vehicle cells had no action and read order.photo_url (in the tables commit)
  • B4 4d1ec98 the vehicle and driver panel-header anchors had no click handler (in the details commit)
  • B5 6d52f3d work-order "Linked Schedule" never rendered; the schedule is loaded by uuid
  • B6 4d1ec98 sensor/details showed device.name instead of displayName (in the details commit)
  • B7 9fd9608 order/pill read @resource.* when invoked with @order
  • B8 44a94ee the order-form vehicle picker passed the driver query (in the pickers commit)
  • B9 44a94ee <Image @src> in modals/order-form and order/form/details (in the pickers commit)
  • B10 301c91f modals/place-assign-vendor wrote a record into vendor_uuid
  • B11 c645155 identity stubs put UUIDs in identifier fields (in the tables commit)
  • also ad352d3: the device panel header fell back to attachable_uuid as the asset name

Removed cell API

compact, showStatusBadge and showStatus on identity-cell columns are accepted and ignored; the dot carries the status and there is never a status badge. assignedVehicleLabel is gone (the descriptor supplies the badge).

Listed, not fixed

  • Plain-text related records remain in order/kanban-card, order/schedule-card (which also nests anchors inside an anchor), widget/live-fleet, the operations monitor sidebar, positions-replay, global-search, customer/orders, the orchestrator panels and map/leaflet-live-map.
  • The telematics attachments rows and the inspection-submission follow-up cards are themselves click targets with rich content, so they keep text.
  • warranty/details subject stays text: the model has subject_type/subject_uuid but no relation.
  • Other resources' own-row name columns (table/cell/anchor, table/cell/media-name) and the drivers fleets link-list.
  • The placeholder "it renders" modal tests (order-form, driver-assign-vehicle, driver-assign-order, attach-device, place-assign-vendor) assert nothing about pickers and were left alone.

Tests

Not run here: the FleetOps CI only lints and builds, and the tests import the registry from ember-ui 0.4.1. Added: tests/unit/utils/resource-descriptors-test.js (all 67 model names resolve, aliases, fleet-ops:* and PHP class strings, every descriptor tolerates {}, no UUID identifiers), tests/unit/instance-initializers/register-fleetops-resource-descriptors-test.js, a rewritten tests/integration/components/cell/resource-identities-test.js (one-line assertions, click chain, self-suppression), tests/integration/components/resource-wrappers-test.js (all four wrappers of every key, Order::Pill @order regression), and an updated identity-columns-test.js (no removed keys, stubs without UUIDs).

Verification

  • eslint ., ember-template-lint ., normalize-rgba-alpha --check && stylelint, fleetbase-intl-lint: all exit 0.
  • Checklist greps: no cell/driver-name, cell/vehicle-name, cell/attached-vehicle, cell/telematic-device, cell/telematic-provider, filter/multi-model, resource/detail-link or config.defaultValues.*Image references; Resource::* and Table::Cell::Identity are invoked only from the wrapper files; the remaining table/cell/anchor columns are own-row name columns.
  • CI: see the comment below once the checks settle.

Manual test plan

  1. Vehicles table: one-line name cell (small image + dot, name, plate badge, driver badge, no status text); ~350ms hover shows the summary, which can be entered, and its View works; click opens the context panel; the driver column never repeats the row's own vehicle; stub rows with only driver_name still open the driver.
  2. Drivers table mirrors it; vendor opens the vendor panel.
  3. Trailers table: towing vehicle is a vehicle identity cell; vendor no longer throws.
  4. Orders table: driver, vehicle, customer (contact and vendor), facilitator, pickup/dropoff, created by.
  5. Details: vehicle driver pill and header links; driver vehicle and vendor; fuel report reporter user pill; work order target/assignee and a Linked Schedule that now renders; maintenance and schedule; equipment and part fitted-to; sensor and device-event device display name; fleet and service rate; warranty static pill + hover; file/category pills.
  6. Pickers: order form (customer, facilitator, driver, vehicle, places); equipment form type switch updates the trigger; work-order target/assignee, maintenance-schedule, attach telematic device, fleet assign, driver assign order; user and role pickers; rows show photo/icon, title and details; the trigger shows the compact option; clear works.
  7. Filters: vehicles driver multi-filter chips; orders driver, vehicle and customer; events device; chips restore after reload.
  8. Map drawer: a cell click does not also fire the row action.
  9. Hover card: mousing from target to card keeps it open; leaving or Escape hides it; not clipped inside the context panel; above overlays; light and dark; no lag on 100 rows.
  10. Regression: the activity-log popover can still be entered; existing ResourceIdentity users in other engines are unchanged.

One descriptor per resource: how it is titled and identified (never by
UUID), its photo or icon tile, its online flag and status with the tone
map the old identity cells carried, the badges an identity cell shows on
its line, the details a select option lists, the facts a hover summary
shows, and how it opens: the action service's context panel where one
exists, a route transition for inspections and fuel integrations, the
entity modal, or nothing for warranties, manifests and recurring
schedules.

All 67 fleetops-data models are covered: 34 first-class resources, 16
sub-records that delegate opening to their parent (or do not open), 13
polymorphic subtypes registered as aliases of their concrete resource, and
the attachable, facilitator and maintenance-subject bases, which resolve
the concrete descriptor from the record or its *_type attribute and
delegate. Parts and work orders hydrate their fitted asset and linked
schedule lazily, on hover only.

The engine instance-initializer registers them through the
resource-registry service ember-ui 0.4.1 provides, so no ember-ui module
is imported; a host on an older ember-ui has no such service and the
identity components fall back to text.
…t option

Template-only wrappers over the ember-ui bases, one set per descriptor key:
<key>/pill and <key>/summary, cell/<key>-identity and select-option/<key>,
plus the attachable, facilitator and maintenance-subject dispatchers, which
resolve the concrete resource through their descriptor.

The existing vehicle, driver, trailer, device, equipment, order and
inspection-submission pills keep their named arguments but now render
through Resource::Pill, so they open the record and show its summary on
hover; the vehicle tooltip and the order QR body live in the summaries.
The vehicle, driver, device, equipment and part identity cells drop their
JS: their tone maps moved into the descriptors. select-option/user and the
select-option base are gone because ember-ui provides them.
driverActions.panel.view took the argument as a record, so a table cell
that only had a driver_name stub, or a proxy from an async relation, opened
a panel with nothing in it. Resolve thenables and loadResource() stubs
first, as vehicleActions and trailerActions already do.
Every related-record column now renders the resource's identity cell:
vehicles (driver, vendor), drivers (vehicle, vendor), trailers (name as a
trailer, towing vehicle, vendor), fleets (service area, parent fleet,
vendor, zone), fuel reports (reporter, driver, vehicle), fuel transactions
(vehicle, fuel report), issues (reporter, assignee, driver, vehicle),
contacts, customers and vendors (address as its place), orders (payload,
driver, vehicle, customer, facilitator, pickup, dropoff, created and
updated by), service rates (service area, zone), connectivity events,
sensors and telematics (device, provider), the device table columns
(device, attached asset, provider), inspection submissions (form, vehicle,
driver), maintenances (asset, performed by), schedules (subject, default
assignee), work orders (target, assignee), payments (service quote, order,
customer) and the map drawer listings (driver, current job, vehicle).

Rows that only carry a *_name build an identity stub with
buildIdentityStub, which keeps UUIDs out of identifier fields and shares
one in-flight load per row, and read async relations through
relation-value so a promise proxy never passes for a record. Columns drop
the compact, showStatus and showStatusBadge keys the new cell ignores, and
the vehicles and trailers vendor columns open the vendor panel instead of
calling a viewVendor that never existed. The four multi-model filters use
ember-ui's filter/model-multiple.
The work order carries only schedule_uuid and the model has no relation
to it, so the "Linked Schedule" field never rendered. Peek the store for
the schedule, then fetch it, and keep it on the component.
The panel header fell back to attachable_uuid when nothing else named the
asset, which put a UUID in the chip.
A part carries asset_type and asset_uuid with no relation, so the field
could only echo asset_name. Resolve the type through the resource
registry and fetch the record, so the field can show the asset's pill.
Every detail field that names another record renders that record's pill,
which opens it and shows its summary on hover: vehicle (driver, and a new
attached-trailer field), driver (vendor, assigned vehicle), fuel report
(reporter, driver, vehicle), order (customer and facilitator, which are
polymorphic), trailer (category, towing vehicle, vendor, warranty), work
order (target, assignee, linked schedule), maintenance (asset, performed
by, work order), maintenance schedule (subject, default assignee),
equipment (equipped to, warranty), part (fitted to, vendor, warranty),
sensor (device, warranty), device (warranty), device event (device,
provider), fleet (service area, zone), service rate (order config, zones
and service areas), inspection submission (form, submitted by), warranty
(vendor), the device vehicle tab (driver, device), and the fuel
transaction and connectivity event templates.

The vehicle and driver panel headers had anchors styled as links with no
click handler; they are pills now, as are the sensor, device and work
order header chips. Sensor details showed the device's raw name instead
of its display name. Async relations are read through resource-relation
so a promise proxy never renders as a record.

Inside clickable cards (vehicle and trailer cards, inspection history,
fleet listings) a nested click target is not allowed, so those show the
compact select-option row instead.
modals/place-assign-vendor wrote the selected vendor record into
vendor_uuid, so saving the place sent an object where the API expects an
id. Use ModelSelect's onChangeId. The picker also renders the vendor
select-option.
All 128 ModelSelect pickers and the eight record-backed PowerSelects now
render each option through select-option/<key> and show the compact
variant in the closed trigger, the way modals/inspection-link already did.
Polymorphic pickers (equipment, maintenance, maintenance schedule, part
and work order forms, attach telematic device) resolve the option
component from the chosen model name with resource-component and fall
back to the label text, so the trigger updates when the type changes. The
six modals that rendered a pill or an order card as the option use the
select-option too, and the position drawer's trackable wrapper dispatches
on its model name.

Along the way: the order form's vehicle picker passed the driver query
(and a this.driversQuery that the template-only modal never had), and two
vehicle pickers set <Image @src>, which ember-ui's Image ignores; both
render the vehicle select-option now. The hide-from-trigger and
normalize-in-trigger classes are gone from every converted picker.
The select-option base and select-option/user, the pill hover style and
the select-option styles moved into ember-ui 0.4.1; filter/multi-model is
ember-ui's filter/model-multiple; cell/driver-name, cell/vehicle-name and
cell/telematic-provider are replaced by identity cells; and
cell/attached-vehicle and cell/telematic-device had no callers left. A
grep for each name finds no remaining reference.
Every fact a FleetOps descriptor lists names its label with a
resource-summary.facts.<key> translation key; the summary falls back to a
humanized key when the host has no translation.
A unit test walks all 67 fleetops-data model names through the registry
and checks each descriptor tolerates an empty record and never returns a
UUID as an identifier; the instance-initializer test counts the 53
descriptors it registers. The identity-cell test now asserts one line:
plate and driver on the vehicle line, the vehicle badge on the driver
line, no status text, and the click chain stopping propagation. A
parameterised render test mounts the pill, summary, identity cell and
select option of every key, plus the Order::Pill @order regression. The
identity-columns test drops the compact, showStatus and
assignedVehicleLabel expectations and checks stubs carry no UUID.

These tests are not run by the FleetOps CI, which only lints and builds;
they need ember-ui 0.4.1 in node_modules to import the registry.
@roncodes

Copy link
Copy Markdown
Member Author

CI verdict: the Ember.js CI build (lint + build) passed on the first run even though the published @fleetbase/ember-ui is still 0.3.41, because the branch imports no new ember-ui module and resolves the registry service at runtime. The PHP CI failure is TelematicServiceTelemetryTest in the server tests, which this branch does not touch; the same test fails on release/v0.6.66 itself (run 34970294405). The Postman contract job is pending on the live API secret. The added Ember tests are not run by CI and need ember-ui 0.4.1 in node_modules.

The base branch rewrote the fuel transaction review page and the fuel
provider transactions controller now derives its columns from the parent
controller. Keep the upstream versions and reapply the identity work on
top: the parent controller's vehicle and fuel report identity columns
survive the merge, and the rewritten review page shows the vehicle, order
and fuel report as pills beside their match buttons.
The picker switches between vehicles and orders, so it resolves the
select-option from the model name like the other polymorphic pickers.
The vehicles index built a column whose action was vendorActions.panel.view,
but the controller never injected vendorActions, so opening the route threw
"Cannot read properties of undefined (reading 'panel')" while rendering the
table. The store was read the same way.
…te-fns

Inline badges next to an identity cell now come only from the driver, vehicle
and trailer descriptors; places, contacts, devices and the rest render just
their name. The descriptor dates (licence expiry, position and schedule
identifiers) go through the console's date-fns formatter instead of the
browser locale, which had produced Arabic digits and a garbled expiry.
The .driver-card hover colours were written for the dark orchestrator
workbench; the drivers index card shares the class, so hovering it in light
mode painted a dark slab. Light mode gets gray-100/gray-300 and the dark pair
is scoped to the dark theme.
The column had no resourcePath, so the identity cell fell back to the row and
showed the device's own name and icon under "Telematic Provider". It now
resolves the loaded telematic, or a stub that loads it.
The vehicle, order and fuel report pills received a bare relation that is
usually not loaded, so they rendered a fallback title with no link and no
summary. The controller now hands them the loaded record or an identity stub
that carries the transaction's name and loads the record on hover or click.
These three tables still used table/cell/media-name; they now use the
vendor, contact and customer identity cells like every other index.
…e driver pill

- vendor/pill and vendor/summary wrappers existed for integrated vendors only,
  so an order whose facilitator is a plain vendor threw "Attempted to resolve
  vendor/pill" and crashed the details view.
- The hidden Trailers and Devices columns on vehicles and trailers rendered
  "[object Array]" once shown; they now use cell/resource-list, one pill per
  related record.
- Contact and customer popovers showed raw type strings such as
  "customer-fleet-ops:contact"; they now read "Customer" or "Contact".
- The vehicle details driver pill received a bare belongs-to proxy; it now
  gets the loaded relation so an unassigned driver renders static.
They were written with the previous commit but a vendor/ ignore rule kept
them out of the index; force-added so vendor/pill resolves.
…ell, titleize column labels

- The trailer, inspection and long-offline badge rules leave the engine
  stylesheet; ember-ui's palette now owns every Fleet-Ops status.
- cell/resource-list pills draw a 1.25rem circle with the name centred
  beside it and a hover box that stays inside the cell.
- Column labels read in title case: Device Attached, Driver Assigned,
  Trailer Attached, the trailer columns and Attached Asset.
@roncodes
roncodes merged commit feef2d8 into release/v0.6.66 Sep 16, 2026
6 checks passed
@roncodes
roncodes deleted the feature/resource-identity-system branch September 16, 2026 08:15
@codecov

codecov Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (7c5bf7a) to head (d5ad241).
⚠️ Report is 45 commits behind head on release/v0.6.66.

Additional details and impacted files
@@                 Coverage Diff                  @@
##             release/v0.6.66      #324    +/-   ##
====================================================
  Coverage             100.00%   100.00%            
- Complexity             10774     11073   +299     
====================================================
  Files                    565       576    +11     
  Lines                  41545     42186   +641     
====================================================
+ Hits                   41545     42186   +641     
Flag Coverage Δ
backend 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant