156 lines
4.0 KiB
TypeScript
156 lines
4.0 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
import type { ColumnDef } from '@tanstack/react-table';
|
|
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { cn } from '@/lib/utils';
|
|
import type {
|
|
TicketExtensionRequest,
|
|
TicketExtensionRequestStatus,
|
|
} from '@/types';
|
|
import { formatDate } from '@/utils/date';
|
|
|
|
const statusConfig: Record<
|
|
TicketExtensionRequestStatus,
|
|
{ label: string; className: string }
|
|
> = {
|
|
pending: {
|
|
label: 'Pending',
|
|
className:
|
|
'border-amber-200 bg-amber-100 text-amber-800 dark:border-amber-900 dark:bg-amber-950/50 dark:text-amber-300',
|
|
},
|
|
approved: {
|
|
label: 'Approved',
|
|
className:
|
|
'border-emerald-200 bg-emerald-100 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/50 dark:text-emerald-300',
|
|
},
|
|
rejected: {
|
|
label: 'Rejected',
|
|
className:
|
|
'border-red-200 bg-red-100 text-red-800 dark:border-red-900 dark:bg-red-950/50 dark:text-red-300',
|
|
},
|
|
cancelled: {
|
|
label: 'Cancelled',
|
|
className: 'border-border bg-muted text-muted-foreground',
|
|
},
|
|
};
|
|
|
|
function StatusBadge({ status }: { status: TicketExtensionRequestStatus }) {
|
|
const config = statusConfig[status];
|
|
|
|
return (
|
|
<Badge variant="outline" className={cn('border', config.className)}>
|
|
{config.label}
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
function PersonCell({
|
|
name,
|
|
role,
|
|
}: {
|
|
name: string | null | undefined;
|
|
role: string | null | undefined;
|
|
}) {
|
|
return (
|
|
<div className="min-w-0">
|
|
<p className="truncate font-medium">{name || '-'}</p>
|
|
{role ? (
|
|
<p className="truncate text-xs text-muted-foreground">{role}</p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function useExtensionRequestColumns(): ColumnDef<TicketExtensionRequest>[] {
|
|
return useMemo(
|
|
() => [
|
|
{
|
|
accessorKey: 'ticket_name',
|
|
header: 'Ticket / Lot',
|
|
size: 170,
|
|
cell: ({ row }) => (
|
|
<div>
|
|
<p className="font-medium">{row.original.ticket_name}</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
Lot #{row.original.assignment_id}
|
|
</p>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
id: 'requested_by',
|
|
header: 'Requested By',
|
|
size: 170,
|
|
meta: { disableTruncate: true },
|
|
cell: ({ row }) => (
|
|
<PersonCell
|
|
name={row.original.requested_by?.name}
|
|
role={row.original.requested_by?.role.name}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'status',
|
|
header: 'Status',
|
|
size: 125,
|
|
meta: { disableTruncate: true },
|
|
cell: ({ row }) => <StatusBadge status={row.original.status} />,
|
|
},
|
|
{
|
|
accessorKey: 'current_due_at',
|
|
header: 'Current Due Date',
|
|
size: 175,
|
|
cell: ({ row }) => formatDate(row.original.current_due_at),
|
|
},
|
|
{
|
|
accessorKey: 'requested_due_at',
|
|
header: 'Requested Due Date',
|
|
size: 175,
|
|
cell: ({ row }) => formatDate(row.original.requested_due_at),
|
|
},
|
|
{
|
|
accessorKey: 'reason',
|
|
header: 'Request Reason',
|
|
size: 260,
|
|
},
|
|
{
|
|
id: 'resolved_by',
|
|
header: 'Resolved By',
|
|
size: 170,
|
|
meta: { disableTruncate: true },
|
|
cell: ({ row }) => (
|
|
<PersonCell
|
|
name={row.original.resolved_by?.name}
|
|
role={row.original.resolved_by?.role.name}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'decision_note',
|
|
header: 'Decision Note',
|
|
size: 220,
|
|
cell: ({ row }) => row.original.decision_note || '-',
|
|
},
|
|
{
|
|
id: 'previous_requests',
|
|
header: 'Previous Requests',
|
|
size: 150,
|
|
cell: ({ row }) => (
|
|
<Badge variant="secondary">
|
|
{row.original.history_meta.previous_request_count}
|
|
</Badge>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'resolved_at',
|
|
header: 'Resolved On',
|
|
size: 175,
|
|
cell: ({ row }) => formatDate(row.original.resolved_at),
|
|
},
|
|
],
|
|
[],
|
|
);
|
|
}
|