[CLAUDE] Office: P11-D ItTicket auto-assign round-robin + SLA timer (Wave 2, Mig 46)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m17s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m17s
Mig 46 AddSlaFieldsToItTicket (SlaDueAt/SlaWarnedSent/SlaBreached). CreateItTicketHandler: round-robin least-loaded assign cho IT staff (dept Code=IT, tie-break Id) + SlaDueAt theo Priority (Urgent 4h/High 8h/Medium 24h/Low 72h). ItTicketSlaJob background (breach+warning notify, KHONG auto-transition). PUT /{id}/assign admin override. DbInitializer seed dept IT + 2 sample staff (nv.cao/nv.truong). FE ItTicketsPage +MaTicket+assignee+SLA badge (2 app SHA256 mirror). +9 test (191->200). Self-review PASS (seed<->query dept-code verified; em main solo review do session-limit kill reviewer-spawn).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
// Ticket CNTT — Phase 10.3 G-O6 (S38 2026-05-28).
|
||||
// SKELETON Phase 1: read-only kanban list. Auto-assign + SLA timer DEFER Phase 11.
|
||||
// Ticket CNTT — Phase 10.3 G-O6 (S38) + P11-D auto-assign round-robin + SLA timer (S52).
|
||||
// Read-only kanban list + MaTicket + người xử lý (auto-assign dept IT) + SLA badge (đỏ khi quá hạn).
|
||||
// File MIRROR SHA256 identical fe-user counterpart.
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Ticket } from 'lucide-react'
|
||||
@ -11,6 +11,12 @@ import {
|
||||
IT_TICKET_STATUS_LABELS, type ItTicketDto, type PagedResult,
|
||||
} from '@/types/workflowApps'
|
||||
|
||||
function formatSlaDue(iso: string): string {
|
||||
return new Date(iso).toLocaleString('vi-VN', {
|
||||
day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
export function ItTicketsPage() {
|
||||
const list = useQuery({
|
||||
queryKey: ['it-tickets'],
|
||||
@ -29,10 +35,6 @@ export function ItTicketsPage() {
|
||||
<div className="space-y-4">
|
||||
<PageHeader title="Ticket CNTT" description="Helpdesk — báo lỗi và yêu cầu hỗ trợ kỹ thuật" />
|
||||
|
||||
<div className="rounded-lg border bg-amber-50/50 p-3 text-sm text-amber-900">
|
||||
⚠️ <strong>Skeleton Phase 1 (S38):</strong> Read-only list. Form tạo ticket + Auto-assign round-robin + SLA timer defer Phase 11 polish.
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-3">
|
||||
{[1, 2, 3, 5, 4].map((statusKey) => (
|
||||
<div key={statusKey} className="rounded-lg border bg-card p-3">
|
||||
@ -56,6 +58,22 @@ export function ItTicketsPage() {
|
||||
<div className="text-muted-foreground">
|
||||
{IT_TICKET_CATEGORY_LABELS[t.category]} · {t.requesterFullName}
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-1 pt-0.5">
|
||||
<span className="text-muted-foreground truncate" title={t.assignedToFullName ?? undefined}>
|
||||
👤 {t.assignedToFullName ?? <span className="italic">Chưa giao</span>}
|
||||
</span>
|
||||
{t.slaDueAt && (
|
||||
<span
|
||||
className={cn(
|
||||
'rounded px-1.5 py-0.5 text-[10px] whitespace-nowrap',
|
||||
t.slaBreached ? 'bg-red-100 text-red-700 font-medium' : 'bg-slate-100 text-slate-600',
|
||||
)}
|
||||
title={`Hạn xử lý SLA: ${formatSlaDue(t.slaDueAt)}`}
|
||||
>
|
||||
{t.slaBreached ? 'Quá hạn SLA' : `SLA ${formatSlaDue(t.slaDueAt)}`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -66,7 +84,7 @@ export function ItTicketsPage() {
|
||||
{!list.isLoading && items.length === 0 && (
|
||||
<div className="rounded-lg border bg-card p-8 text-center text-muted-foreground">
|
||||
<Ticket className="mx-auto h-10 w-10 mb-3 opacity-50" />
|
||||
Chưa có ticket nào. Form tạo ticket sẽ kích hoạt Phase 11.
|
||||
Chưa có ticket nào.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user