204 lines
8.6 KiB
TypeScript
204 lines
8.6 KiB
TypeScript
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { FileText, CheckCircle, XCircle, Clock, Loader2, Share2 } from 'lucide-react';
|
|
import { format } from 'date-fns';
|
|
import type { SummaryDetails } from '@/services/summaryApi';
|
|
|
|
interface SummaryTabProps {
|
|
summary: SummaryDetails | null;
|
|
loading: boolean;
|
|
onShare?: () => void;
|
|
isInitiator?: boolean;
|
|
}
|
|
|
|
export function SummaryTab({ summary, loading, onShare, isInitiator }: SummaryTabProps) {
|
|
const getStatusIcon = (status: string) => {
|
|
const statusLower = status.toLowerCase();
|
|
if (statusLower === 'approved') return <CheckCircle className="h-4 w-4 text-green-600" />;
|
|
if (statusLower === 'rejected') return <XCircle className="h-4 w-4 text-red-600" />;
|
|
if (statusLower === 'pending' || statusLower === 'in progress') return <Clock className="h-4 w-4 text-orange-600" />;
|
|
return <FileText className="h-4 w-4 text-gray-600" />;
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
const statusLower = status.toLowerCase();
|
|
if (statusLower === 'approved') return 'bg-green-100 text-green-700 border-green-300';
|
|
if (statusLower === 'rejected') return 'bg-red-100 text-red-700 border-red-300';
|
|
if (statusLower === 'pending' || statusLower === 'in progress') return 'bg-orange-100 text-orange-700 border-orange-300';
|
|
return 'bg-gray-100 text-gray-700 border-gray-300';
|
|
};
|
|
|
|
// Helper function to get designation or department (fallback to department if designation is N/A or empty)
|
|
const getDesignationOrDepartment = (designation?: string | null, department?: string | null) => {
|
|
if (designation && designation.trim() && designation.trim().toUpperCase() !== 'N/A') {
|
|
return designation;
|
|
}
|
|
if (department && department.trim() && department.trim().toUpperCase() !== 'N/A') {
|
|
return department;
|
|
}
|
|
return 'N/A';
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="text-center">
|
|
<Loader2 className="h-12 w-12 animate-spin text-blue-600 mx-auto mb-4" />
|
|
<p className="text-gray-600">Loading summary...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!summary) {
|
|
return (
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="text-center">
|
|
<FileText className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
|
<h2 className="text-xl font-bold text-gray-900 mb-2">Summary Not Available</h2>
|
|
<p className="text-gray-600">Summary has not been generated for this request yet.</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Summary Card */}
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
|
<div className="p-6 border-b border-gray-200">
|
|
<div className="flex items-start justify-between gap-4 mb-4">
|
|
<div>
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">{summary.title}</h2>
|
|
<p className="text-sm text-gray-600">Request #{summary.requestNumber}</p>
|
|
</div>
|
|
{isInitiator && onShare ? (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={onShare}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Share2 className="w-4 h-4" />
|
|
<span>Share</span>
|
|
</Button>
|
|
) : (
|
|
<Badge className={getStatusColor(summary.workflow.status)}>
|
|
{getStatusIcon(summary.workflow.status)}
|
|
<span className="ml-1 capitalize">{summary.workflow.status}</span>
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
{summary.description && (
|
|
<p className="text-gray-700 mb-4">{summary.description}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Initiator Section */}
|
|
<div className="p-6 border-b border-gray-200">
|
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">Initiator</h3>
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Name</p>
|
|
<p className="text-sm font-medium text-gray-900">{summary.initiator.name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Designation</p>
|
|
<p className="text-sm font-medium text-gray-900">
|
|
{getDesignationOrDepartment(summary.initiator.designation, summary.initiator.department)}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Status</p>
|
|
<p className="text-sm font-medium text-gray-900">{summary.initiator.status}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Time Stamp</p>
|
|
<p className="text-sm font-medium text-gray-900">
|
|
{format(new Date(summary.initiator.timestamp), 'MMM dd, yy, HH:mm')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Approvers Section */}
|
|
{summary.approvers && summary.approvers.length > 0 && (
|
|
<div className="p-6 border-b border-gray-200">
|
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">Workflow</h3>
|
|
{summary.approvers.map((approver, index) => (
|
|
<div key={index} className="mb-6 last:mb-0">
|
|
<h4 className="text-md font-semibold text-gray-800 mb-3">
|
|
{approver.levelName || `Approver ${approver.levelNumber}`}
|
|
</h4>
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-3">
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Name</p>
|
|
<p className="text-sm font-medium text-gray-900">{approver.name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Designation</p>
|
|
<p className="text-sm font-medium text-gray-900">
|
|
{getDesignationOrDepartment(approver.designation, approver.department)}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Status</p>
|
|
<div className="flex items-center gap-1">
|
|
{getStatusIcon(approver.status)}
|
|
<p className="text-sm font-medium text-gray-900">{approver.status}</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Time Stamp</p>
|
|
<p className="text-sm font-medium text-gray-900">
|
|
{format(new Date(approver.timestamp), 'MMM dd, yy, HH:mm')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Remarks</p>
|
|
<p className="text-sm text-gray-700">{approver.remarks || '—'}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Closing Remarks Section */}
|
|
<div className="p-6">
|
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">Closing Remarks (Conclusion)</h3>
|
|
<div className="bg-gray-50 rounded-lg p-4">
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Name</p>
|
|
<p className="text-sm font-medium text-gray-900">{summary.initiator.name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Designation</p>
|
|
<p className="text-sm font-medium text-gray-900">
|
|
{getDesignationOrDepartment(summary.initiator.designation, summary.initiator.department)}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Status</p>
|
|
<p className="text-sm font-medium text-gray-900">Concluded</p>
|
|
</div>
|
|
{summary.isAiGenerated && (
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Source</p>
|
|
<Badge variant="outline" className="text-xs">AI Generated</Badge>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-500 mb-1">Remarks</p>
|
|
<p className="text-sm text-gray-700 whitespace-pre-wrap">{summary.closingRemarks || '—'}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|