Skip to content

Replace the Resources Hub with Radar - #325

Merged
roncodes merged 13 commits into
release/v0.6.66from
feature/resources-hub-refactor-design-ad15e9
Sep 16, 2026
Merged

roncodes merged 13 commits into
release/v0.6.66from
feature/resources-hub-refactor-design-ad15e9

Conversation

@roncodes

@roncodes roncodes commented Sep 16, 2026

Copy link
Copy Markdown
Member

Summary

The Resources Hub was a landing page of counts and links. Radar is the triage list behind them, built from the "Fleet Health Concepts" design review: Ops Desk (inbox) as the page, the Morning Brief as a strip on top, and Agenda/Handover as a view toggle, on the console's own palette and dark-mode conventions.

  • List. One row per gap across resources, maintenance, inspections, staffing, compliance, fuel and parts, grouped by due date. Count pills are the only filter and combine in the URL; Open / Snoozed / Resolved tabs; saved views per user. A row's hover rail carries the record's own action (raise the work order, assign the vehicle, match the transaction, send the PIN) beside acknowledge, snooze and assign. A drawer shows failed inspection items or the shift behind a row. Selection turns the rail into a bulk bar; J K X E S A ⏎ work the list from the keyboard.
  • Morning brief. Each category scored with visible arithmetic and a delta against yesterday, a few linked sentences about the day, and one decision per card — each a single existing endpoint call, with an undo strip where reversible.
  • Agenda. The same items on the next 24 hours or 7 days: an overdue band left of now, four lanes, a later rail, an anytime tray a drop can schedule from. Every live shift is a bar; a shift ending with orders still assigned gets a handover card naming the nearest on-shift driver with capacity, with reassign / extend shift / snooze.
  • Inspections are first-class input: failed submissions without follow-up, unresolved follow-ups, stale drafts, links expiring unused, vehicles flagged inspection_failed, inspection-type schedules due.
  • Widget "Radar · N overdue" and a header shortcut.

Items are computed live from existing records (RadarRules, pure and fixture-tested); state (acknowledge, snooze, assign, plan) lives on the core alerts table and auto-resolves when a gap closes. Each source loads in its own try/catch so one failing table degrades the page instead of blanking it.

Three bugs the rules exposed are fixed: Driver::currentShift() filtered on a status the schedule_items enum does not have, parts had three different definitions of "low stock", and inspection schedules produced preventive_maintenance work orders.

Depends on

fleetbase/core-api#258 (branch feature/alerts-snooze-assignment, targeting release/v1.6.62; adds snoozed_until, snoozed_by_uuid, assigned_to_uuid, planned_at to alerts, plus unsnooze(), assignTo(), snoozed() / active() scopes, and acknowledge() now sets status = acknowledged). Land and migrate that first.

Test plan

  • PHP: RadarRulesTest, RadarItemStateTest, RadarBriefingTest, RadarAgendaTest, RadarControllerTest, RadarRoutesTest (41 tests), plus the existing hub, command, driver-scheduling, model-accessor and route-registration suites
  • Ember: 35 Radar tests (components, modifier, controller, utils, widget) in headless Chrome; sidebar rename tests updated
  • npm run lint (js, hbs, css, intl)
  • Live: migrate core-api + fleetops, octane:reload, open /fleet-ops/manage; walk pills, tabs, saved views, rail actions, keyboard, bulk, drawer, brief decisions, agenda drag and handover card; dark mode; widget on the dashboard

Notes for reviewers: the dummy test app now loads only en-us translations (tests/dummy/config/ember-intl.js) because Chrome 152 ships no Intl data for mn and ember-intl hydrates every locale at boot, which took every rendering test down; the sidebar's operations-monitor component gained the app/ re-export it was missing. The one remaining red test in the hub-index unit module (a devices controller injecting hostRouter) is pre-existing.

Radar replaces the Resources Hub with the gaps a fleet manager has to
decide about, one item per record: maintenance and inspection schedules
due, overdue or blocked work orders, open issues, failed inspections with
no follow-up, unresolved inspection follow-ups, stale drafts, inspection
links expiring unused, vehicles flagged inspection_failed, drivers late
for a shift, on shift without a vehicle, or ending a shift with orders
still assigned, licences and leases expiring, unmatched fuel, low stock,
and the notices people write themselves.

RadarRules is pure: it takes plain rows and a clock and answers with
items, pill counts and a summary, so every rule is tested against a
fixture and a fixed morning. RadarItemState is the one piece that touches
the database: acknowledge, snooze, assign and plan live on the core
alerts table, a row created only when someone acts, found again by the
item key, and resolved automatically when the record's gap closes.
RadarController loads each source in its own try/catch so one failing
table degrades the page rather than blanking it.

Three fixes the rules exposed: a driver's current shift filtered on a
status the schedule_items enum does not have; a part had three different
definitions of "low stock"; an inspection schedule produced a preventive
maintenance work order.
The brief is arithmetic, not a black box: each category row scores its
open gaps by severity (capped per rule), the overall score is the mean,
and yesterday's score comes from a per-company setting so the delta is
real. Three or four sentences name the records behind the morning with
links, and the decision cards each describe one call the console can make
in a click (raise the work order for a failed inspection, open a work
order for an overdue schedule, put an idle vehicle on a driver without
one, match a fuel transaction to the vehicle its card belongs to), with
the undo where there is one.

The agenda answers the same items placed on the next 24 hours or 7 days:
an overdue band left of now, four lanes, a later rail, and an anytime
tray for undated work, which a drop gives a time via `planned_at`. Every
live shift becomes a bar on the shifts lane; a shift ending with orders
still assigned gets a handover card naming the nearest on-shift driver
with capacity, and an endpoint extends a shift by the hour.

Also: items can be narrowed to one category or to the caller's own
assignments, and driver subjects carry a phone for the Call action.
The hub was a landing page of counts and links. Radar is the triage
list behind them: one row per gap, grouped by due date, with count pills
as the only filter, Open / Snoozed / Resolved tabs, and saved views kept
per user. A row's hover rail carries the record's own action (raise the
work order, assign the vehicle, match the transaction, send the PIN)
beside acknowledge, snooze and assign; a drawer shows the failed
inspection items or the shift behind a row. Selection turns the rail
into a bulk bar, and J/K/X/E/S/A/Enter work the list from the keyboard.

Above the list the morning brief scores each category with visible
arithmetic, writes a few linked sentences about the day, and offers one
decision per card, each a single call with an undo strip. The Agenda
toggle lays the same items on the next 24 hours or 7 days: an overdue
band, four lanes, a later rail and an anytime tray a drop can schedule
from, with every live shift as a bar and a handover card naming the
nearest driver with capacity to take a leaving driver's orders.

Styling stays on the console's palette: sky-500 for the active pill and
primary actions, the badge palette for chips and severities, the hub's
rose and amber tones for overdue and today, and dark mode through the
same body[data-theme] overrides as the rest of the engine.
"Radar · 3 overdue" as a KPI tile that links to the page, registered
with the other tiles and as a Fleet-Ops header shortcut.

Two things the test build needed: the dummy app now loads only en-us
translations, because ember-intl hydrates every locale at boot and
Chrome 152 ships no Intl data for `mn`, which took every rendering test
down before it began; and the sidebar's operations-monitor component
gets the app re-export every engine component needs to resolve outside
the engine.
Acknowledging or snoozing a Radar item failed with "Unknown column
'public_id'". Fleetbase\Models\Alert uses HasPublicId, whose creating
hook checks alerts.public_id for a collision, but the core migration
never created the column, so every alert insert failed: Radar's state
rows, and the low-stock, sensor and device event alerts FleetOps
already raises. Add it, guarded so it is a no-op wherever core-api adds
it itself.
- The header's search, view toggle, refresh and new-notice controls sit
  at the right edge again: the hub header aligns its children to the
  start at 768px and up, which shrank Radar's row to its content.
- Search, toggle and buttons share one 2rem height, and the search text
  clears the magnifying glass; ember-ui's form-input-sm padding was
  winning on specificity.
- Last sync and the snooze toast format times with date-fns instead of
  the browser locale, which rendered Arabic digits.
- Rows get a real checkbox, so selection and bulk actions work; the
  Font Awesome regular square never rendered.
- Rail buttons, including the snooze dropdown, line up at one height,
  the open-record icon is smaller, and saved views are shorter with the
  save button keeping its padding.
- Keyboard hints, the count and paging float in a container pinned to
  the bottom of the viewport.
- Rows, decision cards, handover cards and brief links open the record
  in a context panel instead of navigating away from Radar.
- The select modal renders options with the resource select-option
  components.
PHP CI installs core-api 1.6.55 from composer.lock, which predates the
alert snooze and assignment API. RadarItemStateTest died on "undefined
relationship [assignedTo]", and the same calls would fail wherever that
core is deployed. RadarItemState now writes the state columns itself
(acknowledge, snooze, wake, assign, plan, resolve), resolves user names
from one uuid lookup instead of the new relations, and reads dates
through RadarRules::carbon rather than model casts. The controller
routes every state change through it.

Coverage was far short of the gate: the probe tests replace the
controller's loaders and plumbing, so none of that ran. A new database
test drives the real controller over in-memory SQLite: every source
loader, handover orders, shift extension, company membership, notices,
score history and the failure paths. Edge-case tests cover the
remaining branches in the rules, brief, agenda and state helpers. All
Radar files are at 100% lines and methods, and the full suite passes
against core-api 1.6.55.
- Ember CI: merge the duplicate .fleet-ops-radar-body selector stylelint
  rejected.
- Row checkboxes show as checked: in-array takes the item first, and
  Radar passed the list first, so every row read as unselected.
- Shift-click selects or clears every row between the last row toggled
  and this one.
- The keys and paging footer is a full-width bar flush with the bottom
  and side edges, the same 39px height as the sidebar's attribution
  footer so the borders line up.
- Last sync shows the date as well as the time.
- Send PIN asks how to send it, offering only the channels the link's
  recipient can receive, as the inspection form's link list does. It
  used to post without `via` and fail validation.
- Picking a vehicle for a driver no longer shows "-". ModelSelect only
  resolves a uuid on first render, and the modal fed the picked uuid
  back in; it now keeps the chosen record.
@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 (feef2d8) to head (a35a94b).
⚠️ Report is 14 commits behind head on release/v0.6.66.

Additional details and impacted files
@@                  Coverage Diff                  @@
##             release/v0.6.66      #325     +/-   ##
=====================================================
  Coverage             100.00%   100.00%             
- Complexity             11073     11709    +636     
=====================================================
  Files                    576       581      +5     
  Lines                  42186     44307   +2121     
=====================================================
+ Hits                   42186     44307   +2121     
Flag Coverage Δ
backend 100.00% <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.

… details routes

Radar opened records through each resource's panel.view, and most of those
registered only an overview tab with no header, title or buttons, drew the
default header rule above the tab list, and opened narrower than the route.
Work order, maintenance, schedule, part, equipment, issue and device panels now
carry their route's header component, tabs and action buttons; inspection
submissions and forms gain panels. Header menus are shared between routes and
panels so they cannot drift.

Radar loads records by public id with a single-record query, as the details
routes do, which stops Ember Data's RecordIdentifier type error. Closing an
issue from Radar uses the close-issue modal, and selected inspection links can
be revoked in bulk. Adds the missing issue timeline, panel header and card
re-exports the host panel needs.
…ze and fitted geofence maps

Driver, vehicle, trailer, fleet, place, contact, customer, vendor, fuel report,
service area and zone context panels now open with their details route's title,
tabs and actions, and without the header rule above the tabs. The driver,
vehicle, trailer and fleet header menus move into their action services so the
route and the panel share one definition. New tabs: driver activity, fleet
vehicles and drivers, vendor personnel, vehicle inspections.

Opening a facilitator from the orders table froze the page with no error: the
index payload normalizes it as a bare facilitator record, and the polymorphic
descriptor handed it back to registry.open, which resolved it to itself in an
endless promise loop. The base now opens the concrete descriptor with the
concrete record.

Service area and zone panels use a geofence map that re-measures Leaflet once
the overlay settles and fits the whole boundary, so the map is no longer grey
or off-centre, and is taller. The zone's description field shows its
description.

Work order, maintenance and schedule forms can clear their target, assignee and
type selectors. The Radar open-record test follows the panel behaviour.
@roncodes
roncodes merged commit 2b83b3c into release/v0.6.66 Sep 16, 2026
6 checks passed
@roncodes
roncodes deleted the feature/resources-hub-refactor-design-ad15e9 branch September 16, 2026 11:01
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