pmtool / src /components /asset-management /shared /AssignmentTimeline.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
8.44 kB
import React from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Calendar,
User,
Clock,
CheckCircle,
ArrowRight,
Edit,
Trash2,
ExternalLink
} from 'lucide-react';
import { AssetAssignment } from '@/types/asset-management';
import { format, formatDistanceToNow, isAfter, isBefore } from 'date-fns';
interface AssignmentTimelineProps {
assignments: AssetAssignment[];
onEditAssignment?: (assignment: AssetAssignment) => void;
onDeleteAssignment?: (assignment: AssetAssignment) => void;
onNavigateToEmployee?: (employeeId: number) => void;
showActions?: boolean;
}
const AssignmentTimeline: React.FC<AssignmentTimelineProps> = ({
assignments,
onEditAssignment,
onDeleteAssignment,
onNavigateToEmployee,
showActions = true
}) => {
// Sort assignments by assigned date (most recent first)
const sortedAssignments = [...assignments].sort((a, b) =>
new Date(b.assignedDate).getTime() - new Date(a.assignedDate).getTime()
);
const getAssignmentStatus = (assignment: AssetAssignment) => {
if (!assignment.returnedDate) {
return 'active';
}
return 'completed';
};
const getStatusBadge = (assignment: AssetAssignment) => {
const status = getAssignmentStatus(assignment);
if (status === 'active') {
return (
<Badge variant="default" className="bg-green-600">
<Clock className="h-3 w-3 mr-1" />
Active
</Badge>
);
}
return (
<Badge variant="outline">
<CheckCircle className="h-3 w-3 mr-1" />
Completed
</Badge>
);
};
const getDuration = (assignment: AssetAssignment) => {
const startDate = new Date(assignment.assignedDate);
const endDate = assignment.returnedDate ? new Date(assignment.returnedDate) : new Date();
if (!assignment.returnedDate) {
return `${formatDistanceToNow(startDate)} (ongoing)`;
}
const durationMs = endDate.getTime() - startDate.getTime();
const days = Math.floor(durationMs / (1000 * 60 * 60 * 24));
if (days < 1) {
return 'Less than 1 day';
} else if (days === 1) {
return '1 day';
} else if (days < 30) {
return `${days} days`;
} else if (days < 365) {
const months = Math.floor(days / 30);
return months === 1 ? '1 month' : `${months} months`;
} else {
const years = Math.floor(days / 365);
return years === 1 ? '1 year' : `${years} years`;
}
};
if (assignments.length === 0) {
return (
<div className="text-center py-8 text-muted-foreground">
<Calendar className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>No assignment history available.</p>
</div>
);
}
return (
<div className="space-y-4">
{/* Timeline */}
<div className="relative">
{/* Timeline line */}
<div className="absolute left-6 top-0 bottom-0 w-0.5 bg-border"></div>
{sortedAssignments.map((assignment, index) => {
const isActive = getAssignmentStatus(assignment) === 'active';
const isLast = index === sortedAssignments.length - 1;
return (
<div key={assignment.assignmentID} className="relative flex items-start space-x-4 pb-6">
{/* Timeline dot */}
<div className={`
relative z-10 flex items-center justify-center w-12 h-12 rounded-full border-2
${isActive
? 'bg-green-100 border-green-500 text-green-700'
: 'bg-gray-100 border-gray-300 text-gray-500'
}
`}>
{isActive ? (
<Clock className="h-5 w-5" />
) : (
<CheckCircle className="h-5 w-5" />
)}
</div>
{/* Timeline content */}
<div className="flex-1 min-w-0">
<Card className={`${isActive ? 'border-green-200 bg-green-50/50' : ''}`}>
<CardContent className="p-4">
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
{/* Header */}
<div className="flex items-center gap-2 mb-2">
{getStatusBadge(assignment)}
<span className="text-sm text-muted-foreground">
Assignment #{assignment.assignmentID}
</span>
</div>
{/* Employee info */}
<div className="flex items-center gap-2 mb-2">
<User className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">
{assignment.employeeName || `Employee #${assignment.employeeID}`}
</span>
{onNavigateToEmployee && (
<Button
variant="ghost"
size="sm"
className="h-6 px-2 text-xs"
onClick={() => onNavigateToEmployee(assignment.employeeID)}
>
<ExternalLink className="h-3 w-3 mr-1" />
View
</Button>
)}
</div>
{/* Date range */}
<div className="flex items-center gap-2 mb-2 text-sm text-muted-foreground">
<Calendar className="h-4 w-4" />
<span>{format(new Date(assignment.assignedDate), 'PPP')}</span>
{assignment.returnedDate && (
<>
<ArrowRight className="h-3 w-3" />
<span>{format(new Date(assignment.returnedDate), 'PPP')}</span>
</>
)}
</div>
{/* Duration */}
<div className="text-sm text-muted-foreground mb-2">
Duration: {getDuration(assignment)}
</div>
{/* Remarks */}
{assignment.remarks && (
<div className="mt-2">
<p className="text-sm text-muted-foreground font-medium">Remarks:</p>
<p className="text-sm mt-1">{assignment.remarks}</p>
</div>
)}
</div>
{/* Actions */}
{showActions && (
<div className="flex items-center gap-1 ml-4">
{onEditAssignment && (
<Button
variant="ghost"
size="sm"
onClick={() => onEditAssignment(assignment)}
>
<Edit className="h-4 w-4" />
</Button>
)}
{onDeleteAssignment && (
<Button
variant="ghost"
size="sm"
onClick={() => onDeleteAssignment(assignment)}
>
<Trash2 className="h-4 w-4" />
</Button>
)}
</div>
)}
</div>
</CardContent>
</Card>
</div>
</div>
);
})}
</div>
</div>
);
};
export default AssignmentTimeline;