114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
import type { ColumnDef } from '@tanstack/react-table';
|
|
import { CalendarClock, ClipboardCheck } from 'lucide-react';
|
|
|
|
import { Badge } from '@/components/ui/badge';
|
|
import type { TicketListItem } from '@/types';
|
|
|
|
import { formatDate } from '@/utils/date';
|
|
|
|
function TicketAttention({ ticket }: { ticket: TicketListItem }) {
|
|
const summary = ticket.attention_summary;
|
|
const reviewCount = summary?.pending_repair_review_count ?? 0;
|
|
const extensionCount = summary?.pending_extension_request_count ?? 0;
|
|
const showReview = reviewCount > 0;
|
|
const showExtension = extensionCount > 0;
|
|
|
|
if (!showReview && !showExtension) return null;
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-1.5">
|
|
{showReview ? (
|
|
<Badge
|
|
className="gap-1.5 border-0 bg-orange-100 text-orange-700 shadow-none dark:bg-orange-950/60 dark:text-orange-300"
|
|
title={`${reviewCount} pending repair ${reviewCount === 1 ? 'review' : 'reviews'}`}
|
|
>
|
|
<ClipboardCheck aria-hidden="true" />
|
|
Review
|
|
<span className="font-semibold tabular-nums">{reviewCount}</span>
|
|
</Badge>
|
|
) : null}
|
|
{showExtension ? (
|
|
<Badge
|
|
className="gap-1.5 border-0 bg-blue-100 text-blue-700 shadow-none dark:bg-blue-950/60 dark:text-blue-300"
|
|
title={`${extensionCount} pending extension ${extensionCount === 1 ? 'request' : 'requests'}`}
|
|
>
|
|
<CalendarClock aria-hidden="true" />
|
|
Extension
|
|
<span className="font-semibold tabular-nums">+{extensionCount}</span>
|
|
</Badge>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function useTicketColumns(): ColumnDef<TicketListItem>[] {
|
|
return useMemo(
|
|
() => [
|
|
{
|
|
accessorKey: 'ticket_name',
|
|
header: 'Ticket',
|
|
size: 180,
|
|
cell: ({ row }) => (
|
|
<span className="font-medium">{row.original.ticket_name}</span>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'chainage_name',
|
|
header: 'Segment',
|
|
size: 180,
|
|
cell: ({ row }) => row.original.chainage_name || 'N/A',
|
|
},
|
|
{
|
|
accessorKey: 'detection_count',
|
|
header: 'Detections',
|
|
size: 120,
|
|
},
|
|
{
|
|
id: 'attention',
|
|
header: 'Attention',
|
|
size: 210,
|
|
meta: { disableTruncate: true },
|
|
cell: ({ row }) => <TicketAttention ticket={row.original} />,
|
|
},
|
|
{
|
|
accessorKey: 'is_closed',
|
|
header: 'Status',
|
|
size: 100,
|
|
meta: { disableTruncate: true },
|
|
cell: ({ row }) => (
|
|
<Badge variant={row.original.is_closed ? 'secondary' : 'default'}>
|
|
{row.original.is_closed ? 'Closed' : 'Open'}
|
|
</Badge>
|
|
),
|
|
},
|
|
{
|
|
id: 'created_by',
|
|
header: 'Uploaded By',
|
|
size: 220,
|
|
cell: ({ row }) => (
|
|
<div>
|
|
<p className="font-medium">
|
|
{row.original.created_by_name || 'N/A'}
|
|
</p>
|
|
{row.original.created_by_email ? (
|
|
<p className="text-xs text-muted-foreground">
|
|
{row.original.created_by_email}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'updated_at',
|
|
header: 'Updated',
|
|
size: 180,
|
|
cell: ({ row }) => formatDate(row.original.updated_at),
|
|
},
|
|
],
|
|
[],
|
|
);
|
|
}
|