// 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' import { PageHeader } from '@/components/PageHeader' import { api } from '@/lib/api' import { cn } from '@/lib/cn' import { IT_TICKET_CATEGORY_LABELS, IT_TICKET_PRIORITY_BADGE, IT_TICKET_PRIORITY_LABELS, 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'], queryFn: async () => (await api.get>('/it-tickets', { params: { pageSize: 100 } })).data, }) const items = list.data?.items ?? [] // Group by status for kanban-ish display const grouped: Record = { 1: [], 2: [], 3: [], 4: [], 5: [] } items.forEach((t) => { if (grouped[t.status]) grouped[t.status].push(t) }) return (
{[1, 2, 3, 5, 4].map((statusKey) => (

{IT_TICKET_STATUS_LABELS[statusKey]} ({grouped[statusKey].length})

{list.isLoading &&
Đang tải...
} {!list.isLoading && grouped[statusKey].length === 0 && (
Trống
)} {grouped[statusKey].map((t) => (
{t.maTicket ?? '—'} {IT_TICKET_PRIORITY_LABELS[t.priority]}
{t.title}
{IT_TICKET_CATEGORY_LABELS[t.category]} · {t.requesterFullName}
👤 {t.assignedToFullName ?? Chưa giao} {t.slaDueAt && ( {t.slaBreached ? 'Quá hạn SLA' : `SLA ${formatSlaDue(t.slaDueAt)}`} )}
))}
))}
{!list.isLoading && items.length === 0 && (
Chưa có ticket nào.
)}
) }