'use client';
import { useState } from 'react';
import {
AlertCircle,
CalendarDays,
CheckCircle2,
Clock3,
Loader2,
Plus,
RotateCcw,
XCircle,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import type {
TicketExtensionRequest,
TicketExtensionRequestsResponse,
} from '@/types';
import { formatDate } from '@/utils/date';
import { useTicketExtensionRequestQuery } from '../../../hooks/useTicketQueries';
import { ExtensionRequestHistoryDialog } from './ExtensionRequestHistoryDialog';
import { RequestExtensionForm } from './RequestExtensionForm';
interface DetailRowProps {
label: string;
value: string;
children?: React.ReactNode;
className?: string;
}
function DetailRow({ label, value, children, className }: DetailRowProps) {
return (
);
}
const statusConfig = {
pending: {
title: 'Request Pending',
description: 'This extension request is waiting for manager approval.',
icon: Clock3,
className:
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/30 dark:text-amber-300',
},
approved: {
title: 'Request Approved',
description: 'This extension request was approved by the manager.',
icon: CheckCircle2,
className:
'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/30 dark:text-emerald-300',
},
rejected: {
title: 'Request Rejected',
description: 'This extension request was rejected by the manager.',
icon: AlertCircle,
className:
'border-red-200 bg-red-50 text-red-700 dark:border-red-900/60 dark:bg-red-950/30 dark:text-red-300',
},
cancelled: {
title: 'Request Cancelled',
description: 'This extension request was cancelled.',
icon: XCircle,
className: 'border-border bg-muted/50 text-muted-foreground',
},
} satisfies Record<
TicketExtensionRequest['status'],
{
title: string;
description: string;
icon: typeof Clock3;
className: string;
}
>;
export function getLatestRequest(response: TicketExtensionRequestsResponse) {
return response.items.reduce(
(latest, item) => {
if (!latest) return item;
return new Date(item.requested_at) > new Date(latest.requested_at)
? item
: latest;
},
null,
);
}
function getExtensionDays(request: TicketExtensionRequest) {
const difference =
new Date(request.requested_due_at).getTime() -
new Date(request.current_due_at).getTime();
return Math.max(0, Math.ceil(difference / (24 * 60 * 60 * 1000)));
}
export function ExtensionRequestDetails({
request,
requests,
onRequestAnother,
}: {
request: TicketExtensionRequest;
requests: TicketExtensionRequest[];
onRequestAnother?: () => void;
}) {
const config = statusConfig[request.status];
const StatusIcon = config.icon;
const extensionDays = getExtensionDays(request);
return (
{config.title}
{config.description}
Request Details
{extensionDays > 0 ? (
+{extensionDays} {extensionDays === 1 ? 'Day' : 'Days'}
) : null}
{request.resolved_at || request.resolved_by ? (
Manager Response
{request.resolved_at ? (
) : null}
{request.resolved_by ? (
) : null}
{request.status === 'approved' ? (
) : null}
{request.decision_note ? (
) : null}
) : null}
{request.status !== 'pending' && onRequestAnother ? (
) : null}
);
}
export function TicketExtensionRequestPanel({
ticketId,
assignmentId,
dueAt,
canRequest,
}: {
ticketId: string;
assignmentId: number;
dueAt: string | null;
canRequest: boolean;
}) {
const [isRequestFormOpen, setIsRequestFormOpen] = useState(false);
const extensionRequestQuery = useTicketExtensionRequestQuery(
ticketId,
assignmentId,
);
if (extensionRequestQuery.isLoading) {
return (
Loading extension request...
);
}
if (extensionRequestQuery.isError) {
return (
Extension request details could not be loaded.
);
}
const latestRequest = extensionRequestQuery.data
? getLatestRequest(extensionRequestQuery.data)
: null;
if (!latestRequest) {
if (!canRequest) {
return (
No extension request history is available for this ticket lot.
);
}
return (
);
}
if (isRequestFormOpen && latestRequest.status !== 'pending') {
return (
);
}
return (
setIsRequestFormOpen(true) : undefined
}
/>
);
}