Third view on /tools/deployment-history. Where 'Gantt by Project' has
one row per project showing that project's deployments, 'Gantt by Unit'
inverts it — one row per seismograph, bars colored by the project the
unit was deployed to.
The natural use case: "where has BE11529 been across all my jobs?"
Spotting unit rotation patterns, idle gaps, and concurrent assignments
gets immediate visually.
Service
- deployment_history.get_deployment_history_data() now also returns a
`units` array. Each unit dict carries:
{id, bars[], first_active, assignment_count, any_active}
Each bar has the project_name + project_color baked in so the
renderer can paint by job without a second lookup.
- Units sorted: currently-active first, then by first_active ascending.
UI
- Third tab "Gantt by Unit" added next to Calendar / Gantt by Project.
- Tab switcher refactored to a small registry (_DH_TABS) so adding more
views in the future is a one-line addition.
- URL hash sync now supports #gantt and #byunit; nav buttons preserve
the active tab across month-paging.
- SVG layout: 160px label gutter (smaller than the project Gantt's
220px since unit IDs are short), 32px row height, green dot for
units with at least one active deployment. Unit ID is clickable
→ /unit/{id}; each bar is clickable → /projects/{p}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Calendar grid (day-cells with project bars) is great for seeing
which projects had activity on a given day, but bad for seeing how
long any single deployment lasted. The Gantt view inverts that —
one row per project, horizontal bars per assignment window — so an
operator can read durations at a glance.
Service layer
- backend/services/deployment_history.py extends each project's
payload with `bars`: a list of {unit_id, location_id, location_name,
start, end, is_active, source} for every UnitAssignment clipped to
the visible 12-month window. Location names are batch-resolved.
Same cost as before since the underlying assignment scan is the
same; just additional data in the response.
Template
- Tab switcher at the top of /tools/deployment-history toggles
between Calendar and Gantt views. URL hash (#gantt) preserves the
active view across month-nav (Prev / Next / Recent buttons within
the Gantt view link to ?...#gantt to stay on the same tab).
- Gantt view is a plain SVG with:
- Left 220px label gutter: project color dot + truncated name,
whole row clickable → opens the project page
- Right area: horizontal time axis with month gridlines + labels,
"today" dashed orange line, one row per project
- One bar per assignment in that row, colored by project, reduced
opacity for closed assignments, blue outline for metadata-
backfilled assignments, white tip on the right edge of active
bars
- Hover any bar → tooltip with unit + location + window
- Alternating row backgrounds for readability.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The per-unit Gantt chart on /unit/{id} (Phase 1, v0.11.0) was scoped
to one unit's deployment timeline. This adds the fleet-wide view as
a new entry under /tools.
What it shows
- 12-month calendar grid styled like the Job Planner (4 months per
row, responsive down to single column on mobile).
- Each day cell shows up to 4 colored mini-bars — one per project
that had ≥1 active UnitAssignment that day, color deterministically
hashed from project_id. Days with >4 active projects show "+N".
- KPI strip at the top: project count, distinct unit count, total
assignment count in the window.
- Collapsible project legend: ordered by first-active date (which
matches the deployment-history reading order), each row links to
the project page, shows the assignment count.
Click-a-day side panel
- Click any populated day cell → slide-over panel from the right
- Groups by project, lists every (unit, location) active that day
- Per-deployment: unit link, location link, window dates, active /
closed badge, "auto-backfilled" tag for metadata_backfill source
- Sources from a new GET /api/admin/deployment-history/day endpoint
Navigation
- Prev / Next month buttons shift the 12-month window by one month
- "Recent" button jumps back to default (12 months ending now)
- Default window is 11 months back from current month — operator
sees the recent past on first load, not future emptiness
Files
- backend/services/deployment_history.py — data builder + day-detail
helper. Walks UnitAssignment windows, intersects with the 12-month
range, computes per-project active-day sets.
- backend/routers/deployment_history.py — page route + day-detail JSON
endpoint. Wired into main.py.
- templates/admin/deployment_history.html — page + side-panel
- templates/tools.html — new card linking to the page
Phase 3 (deferred): drag-to-resize bars to retroactively adjust
assignment windows from inside the calendar; per-unit row view
(complement to the project-row view) for "where has unit X been across
all jobs"; horizontal scroll for >12-month windows.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>