feat: Wahab demo prep — Cancelled status, phone persistence, admin delete, users picker, detail-page fixes

- 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
This commit is contained in:
root
2026-08-02 14:06:30 +00:00
parent ffed595dbd
commit 1dd88a141e
17 changed files with 391 additions and 48 deletions
+13 -11
View File
@@ -121,6 +121,10 @@
<dt class="text-sm text-gray-500">Reporter</dt>
<dd class="text-sm font-medium text-gray-900" x-text="ticket.reporter || '—'"></dd>
</div>
<div class="flex justify-between" x-show="ticket.phone">
<dt class="text-sm text-gray-500">Phone</dt>
<dd class="text-sm font-medium text-gray-900" x-text="ticket.phone"></dd>
</div>
<div class="flex justify-between">
<dt class="text-sm text-gray-500">Reported Via</dt>
<dd class="text-sm font-medium text-gray-900 capitalize" x-text="ticket.reported_via || '—'"></dd>
@@ -214,6 +218,7 @@
<option value="Wahab Review">Wahab Review</option>
<option value="Closed">Closed</option>
<option value="Reopened">Reopened</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<div>
@@ -328,17 +333,14 @@
},
async loadTechnicians() {
// Users list not exposed via API directly, so use a known list
// In a real system we'd have GET /api/users
this.technicians = [
{ id: 9, full_name: 'Prosper' },
{ id: 10, full_name: 'Sam' },
{ id: 11, full_name: 'Steven' },
{ id: 12, full_name: 'Junior (Samuel)' },
{ id: 13, full_name: 'Francis' },
{ id: 14, full_name: 'Desmond Afful' },
{ id: 15, full_name: 'Afful' },
];
try {
const users = await app().apiGet('/api/auth/users');
// Assign dropdown should only offer Tech-role staff
this.technicians = (users || []).filter(u => u.role === 'Tech');
} catch (e) {
console.error('Technicians load error', e);
this.technicians = [];
}
},
async submitStatusUpdate() {