Wahab customer request (relay #748 v3 next-wave).
3a. Fix 'Tech #N' display:
- FM dashboard's "Technician Workload" card was built from
`Tech #${t.assigned_to}`; it now reads Ticket.assigned_technician_name
(falling back to 'Unassigned', matching /tickets).
- Template sweep confirms no other `Tech #` placeholder exists.
3b. Technician performance dashboard:
- New bearer-gated GET /api/tickets/tech-performance aggregating existing
ticket columns only (no schema change): per-technician total assigned,
completed, in progress, escalated, cancelled, pending, open tasks,
completion rate, and created_at -> closed_at resolution time with explicit
counts for finished tasks lacking a timestamp. Rows key on user id so
same-name technicians stay separate; labels are real names. Fleet totals
and an unassigned-ticket count are included.
- New /dashboard/tech-performance page (FM + CEO nav and links) with sortable
table, completion bars and empty states; same-origin vendored assets only.
- Bucket map, service, schemas and tests in app/services/ticket.py,
app/schemas/ticket.py, tests/test_tech_performance.py.
Tests: 111 passed (101 existing + 10 new).
- Add Cancelled as terminal status reachable from all active states; exclude
from SLA breach reporting and dashboard active counts; add to status pickers
- Persist customer phone on ticket create/update (was silently dropped);
add tickets.phone migration + legacy self-heal guard
- Add admin-only DELETE /api/tickets/{id} (removes timeline/photos/escalations)
- Add GET /api/auth/users for the assign-technician dropdown (was hardcoded)
- TicketOut now returns nested unit/category so the detail page stops showing
'—' for Unit/Property/Category
- Ticket numbering uses max+1 so deletions never re-issue a number
- New-issue form: require Category and (standard mode) Priority client-side
- Tests: 11 new cases covering cancellation, SLA exemption, phone, delete,
users endpoint, numbering
Items 1-5, 9, 11 from denya-wahab-feedback-s2:
- Category.show_in_form (alert-only flag): Gas Leak hidden from issue picker
but kept urgent for SLA/alert and reporting; emergency quick path on the
new-issue form creates urgent tickets via include_hidden categories.
- Seed: Aluminum/Glass, Carpentry, Mould & Damp (Medium default) maintenance
categories; Lost Property renamed Missing Item (+ sub).
- One alembic migration: add show_in_form (backfill True, Gas Leak False) +
data rename Lost Property -> Missing Item.
- Property -> Building -> Apartment cascade with searchable apartment combobox
on the new-issue form and ticket list filters; /api/tickets/units gains
building filter + /units/grouped variant; /api/tickets gains additive
building/unit_id filters. apartment_mapping.json committed (deterministic).
- Group-by-priority toggle on /tickets (four sections + unknown bucket,
age-sortable, composes with filters, URL deep links), FM dashboard active
tickets, and CS dashboard priority card click-through.
- Tests: 13 new (category visibility, seed idempotency/sync, unit grouping,
ticket building/unit filters).
The CEO dashboard requested /api/tickets?page_size=500, but the API caps
page_size at 200 (le=200 in app/routers/tickets.py), so the request
returned 422 and every KPI/chart rendered zeros.
- ceo.html: fetch all tickets by looping pages of page_size=200 until
total items are collected (with a safety bound), keeping KPIs accurate
as volume grows past 200.
- tests: add test suite anchoring the pagination contract — page_size=500
returns 422, page_size=200 returns items/total/page/page_size, and a
page loop collects every ticket without duplicates.
- pyproject: enable pytest-asyncio auto mode and tests/ discovery.
- .gitignore: un-ignore committed tests/test_*.py.
- Fix detail.html to use assigned_technician_name (not .assigned_technician?.full_name)
- Remove double error toast in submitNote() catch block
- Replace wasteful API calls in CEO dashboard with units+data approach
- Remove QR Code option from reported_via dropdown (excluded per scope)