feat: backdated reported date for old active tickets

Wahab can now enter historical tickets that keep their true reported date:

- Add nullable tickets.reported_at (DateTime) via alembic d5e0f2a1c3b4;
  backfill existing rows from created_at so nothing shows empty.
- TicketCreate.reported_at (optional) is persisted by create_ticket and
  defaults to now when omitted, so existing create behavior is unchanged.
- New-issue form gains a 'Reported Date' date picker (defaults to today,
  past dates allowed, future blocked) and sends reported_at in the payload.
- List and detail pages show 'Reported' next to 'Created' (date-only,
  labeled) so backdated tickets are obvious; SLA deadline is unchanged and
  still runs from creation time so backfilling never instantly breaches.
- Tests: backdated create persists + is exposed on list/detail; omitted
  reported_at defaults to now; SLA window unchanged; full datetime round trip.
This commit is contained in:
root
2026-08-03 09:40:23 +00:00
parent 30c0c01c59
commit e0a7479c3e
9 changed files with 217 additions and 3 deletions
+5 -1
View File
@@ -130,6 +130,7 @@
<th class="px-5 py-3 text-left cursor-pointer hover:text-gray-900" @click="sortBy('created_at')">
Created <span x-show="sortField === 'created_at'" x-text="sortDir === 'asc' ? '↑' : '↓'"></span>
</th>
<th class="px-5 py-3 text-left">Reported</th>
<th class="px-5 py-3 text-left">SLA</th>
</tr>
</thead>
@@ -142,6 +143,7 @@
<td class="px-5 py-3 text-gray-600 max-w-xs truncate" x-text="ticket.description || ''"></td>
<td class="px-5 py-3 text-gray-500 text-xs" x-text="ticket.assigned_technician_name || '—'"></td>
<td class="px-5 py-3 text-gray-500 text-xs" x-text="formatDate(ticket.created_at)"></td>
<td class="px-5 py-3 text-gray-500 text-xs" x-text="formatDateShort(ticket.reported_at || ticket.created_at)"></td>
<td class="px-5 py-3">
<span x-show="ticket.sla_deadline" class="text-xs" :class="new Date(ticket.sla_deadline) < new Date() && !['Closed','Completed','Cancelled'].includes(ticket.status) ? 'text-red-600 font-medium' : 'text-gray-400'">
<span x-text="formatDateShort(ticket.sla_deadline)"></span>
@@ -151,7 +153,7 @@
</tr>
</template>
<tr x-show="!tickets.length && !loading">
<td colspan="7" class="px-5 py-16 text-center text-gray-400">
<td colspan="8" class="px-5 py-16 text-center text-gray-400">
<svg class="w-12 h-12 mx-auto text-gray-300 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>
No tickets match your filters
</td>
@@ -178,6 +180,7 @@
<th class="px-5 py-3 text-left">Description</th>
<th class="px-5 py-3 text-left">Assigned To</th>
<th class="px-5 py-3 text-left">Created</th>
<th class="px-5 py-3 text-left">Reported</th>
<th class="px-5 py-3 text-left">SLA</th>
</tr>
</thead>
@@ -190,6 +193,7 @@
<td class="px-5 py-3 text-gray-600 max-w-xs truncate" x-text="ticket.description || ''"></td>
<td class="px-5 py-3 text-gray-500 text-xs" x-text="ticket.assigned_technician_name || '—'"></td>
<td class="px-5 py-3 text-gray-500 text-xs" x-text="formatDate(ticket.created_at)"></td>
<td class="px-5 py-3 text-gray-500 text-xs" x-text="formatDateShort(ticket.reported_at || ticket.created_at)"></td>
<td class="px-5 py-3">
<span x-show="ticket.sla_deadline" class="text-xs" :class="new Date(ticket.sla_deadline) < new Date() && !['Closed','Completed','Cancelled'].includes(ticket.status) ? 'text-red-600 font-medium' : 'text-gray-400'">
<span x-text="formatDateShort(ticket.sla_deadline)"></span>