Files
road-monitoring-ui/src/app/(modules)/ticket/components/TicketColumns.tsx

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),
},
],
[],
);
}