130 lines
5.3 KiB
TypeScript
130 lines
5.3 KiB
TypeScript
/**
|
|
* Upcoming Deadlines Section Component
|
|
* Displays upcoming deadlines with pagination
|
|
*/
|
|
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Progress } from '@/components/ui/progress';
|
|
import { Calendar as CalendarIcon } from 'lucide-react';
|
|
import { UpcomingDeadline } from '@/services/dashboard.service';
|
|
import { Pagination } from '@/components/common/Pagination';
|
|
import { formatHoursMinutes } from '@/utils/slaTracker';
|
|
|
|
interface UpcomingDeadlinesSectionProps {
|
|
isAdmin: boolean;
|
|
upcomingDeadlines: UpcomingDeadline[];
|
|
pagination: {
|
|
page: number;
|
|
totalPages: number;
|
|
totalRecords: number;
|
|
};
|
|
onPageChange: (page: number) => void;
|
|
onNavigate?: (path: string) => void;
|
|
}
|
|
|
|
export function UpcomingDeadlinesSection({
|
|
isAdmin,
|
|
upcomingDeadlines,
|
|
pagination,
|
|
onPageChange,
|
|
onNavigate,
|
|
}: UpcomingDeadlinesSectionProps) {
|
|
if (upcomingDeadlines.length === 0) return null;
|
|
|
|
return (
|
|
<Card className="shadow-md hover:shadow-lg transition-shadow" data-testid="upcoming-deadlines-section">
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<div className="flex items-center gap-2">
|
|
<CalendarIcon className="h-4 w-4 sm:h-5 sm:w-5 text-orange-600" />
|
|
<div>
|
|
<CardTitle className="text-base sm:text-lg">Upcoming Deadlines</CardTitle>
|
|
<CardDescription className="text-xs sm:text-sm">
|
|
{isAdmin ? 'Current active levels organization-wide' : 'Requests awaiting your approval'}
|
|
</CardDescription>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-2 sm:space-y-3">
|
|
{upcomingDeadlines.map((deadline, idx) => {
|
|
const tatPercentage = Number(deadline.tatPercentageUsed) || 0;
|
|
const elapsedHours = Number(deadline.elapsedHours) || 0;
|
|
const remainingHours = Number(deadline.remainingHours) || 0;
|
|
|
|
return (
|
|
<div
|
|
key={idx}
|
|
className="p-3 sm:p-4 border rounded-lg hover:shadow-md transition-all cursor-pointer"
|
|
onClick={() => onNavigate?.(`request/${deadline.requestNumber}`)}
|
|
data-testid={`deadline-item-${deadline.requestNumber}`}
|
|
>
|
|
<div className="flex items-start justify-between gap-2 mb-2">
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center gap-1 sm:gap-2 mb-1 flex-wrap">
|
|
<span className="font-semibold text-xs sm:text-sm">{deadline.requestNumber}</span>
|
|
<Badge
|
|
variant="outline"
|
|
className={`text-xs ${
|
|
deadline.priority === 'express' ? 'bg-orange-50 text-orange-700' : 'bg-blue-50 text-blue-700'
|
|
}`}
|
|
>
|
|
{deadline.priority}
|
|
</Badge>
|
|
</div>
|
|
<p className="text-xs sm:text-sm text-muted-foreground truncate">{deadline.requestTitle}</p>
|
|
<div className="flex items-center gap-2 mt-1 text-xs text-muted-foreground flex-wrap">
|
|
<Badge variant="outline" className="text-xs bg-blue-50 text-blue-700 border-blue-200">
|
|
Level {deadline.levelNumber || '?'}/{(deadline as any).totalLevels || '?'}
|
|
</Badge>
|
|
<span className="truncate">{deadline.approverName}</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-right flex-shrink-0">
|
|
<p className="text-xs text-muted-foreground">TAT Used</p>
|
|
<p
|
|
className={`text-base sm:text-lg font-bold ${
|
|
tatPercentage >= 80 ? 'text-red-600' : tatPercentage >= 50 ? 'text-orange-600' : 'text-green-600'
|
|
}`}
|
|
>
|
|
{tatPercentage.toFixed(0)}%
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<Progress
|
|
value={tatPercentage}
|
|
className={`h-1.5 sm:h-2 ${
|
|
tatPercentage >= 80 ? 'bg-red-100' : tatPercentage >= 50 ? 'bg-orange-100' : 'bg-green-100'
|
|
}`}
|
|
/>
|
|
<div className="flex justify-between text-xs text-muted-foreground">
|
|
<span>{formatHoursMinutes(elapsedHours)} elapsed</span>
|
|
<span>{formatHoursMinutes(remainingHours)} left</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Pagination for Upcoming Deadlines */}
|
|
<div className="mt-4">
|
|
<Pagination
|
|
currentPage={pagination.page}
|
|
totalPages={pagination.totalPages}
|
|
totalRecords={pagination.totalRecords}
|
|
itemsPerPage={10}
|
|
onPageChange={onPageChange}
|
|
itemLabel="deadlines"
|
|
testIdPrefix="dashboard-deadlines-pagination"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|