Round 3 of parity fixes after the user shared side-by-side screen
recordings of our build vs. the original SPA. 72/72 tests, frontend
typecheck/lint/build clean, main entry 16.95 KB gz.
Airtable booking → person linkage (root cause of empty Resource
Availability + Daily Breakdown):
- normalise_booking now tries Resource / Booking Resource / Booked
Resource / Resource (from Booking) / Resource Name (from Resource)
as fallbacks for resourceRecordIds — first non-empty wins. Only
values matching `rec` + 17 chars are kept.
- One-shot LOG_AIRTABLE_SCHEMA_ONCE info log on the first booking
response so we can see what fields the live base actually returns.
Flip to False once we've confirmed the field name.
- Name-based fallback in department.py already in place.
Charts:
- DeptWeeklyChart: two bars per entity. Bar 1 stacks Soft Booked + Active
Booked. Bar 2 stacks Allocated + per-billing-category. Red Avg %
utilisation line crosses both. Legend gains Active/Soft Booked +
Avg %.
- DailyBreakdownModal: two bars per weekday (allocated + billing),
Active Booked + Soft Booked pills at the top, full two-row legend
matching frame f020.
Filters:
- New GlobalFilterBar (Division/Brand/Hub/Role/From/To/Reset) lives
above the tab nav in ProtectedShell, visible on every page.
- New DepartmentFilterBar (Name/Division/Brand/Employment) lives inside
Department.tsx only.
- Resourcing / Bookings lose their redundant inline FilterBar — global
one covers them.
Forecast:
- Pipeline chart bars now stacked by project type (PIPELINE_TYPES
palette). Legend below the chart includes the type colours +
Exiting + Forecast avg.
- New WeeklyComparisonTable below the pipeline: This Week / Next Week
/ Week +2 / Week +3 × project type, Active / Exit counts per cell,
totals row.
- "Last Week" subtitle now reads "Full week actual hours (Mon–Fri)" —
matches the original SPA's semantic.
- Backend: ForecastWeek gains activeAssetsByType + exitingByType maps.
Project Type Summary:
- Selected-type detail panel below the table: avg h/asset + avg
duration tiles (with min–max range), totals line, dept hours
segment bar with colour legend, Insights & Recommended Actions
panel, Panel 1 chart (avg h/asset by completion month, stacked by
division).
- Backend: ProjectTypeStatExtended gains deptHoursBreakdown,
monthlyAvgHoursPerAsset (with byDivision), minDurationDays.
Adhoc People:
- Department page now surfaces a small warning card next to the dept
pills listing the top 6 unmatched Zoho submitter emails + a "+N
more" count.
Header subtitle:
- Reads "<filename> · last updated <localised dd/mm/yyyy hh:mm:ss>"
when parsed_at is present in the parse response. Backend's
/api/timelog/parse now emits parsed_at (ISO 8601). Falls back to
row count if missing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>