Re_Figma_Code/src/pages/Dashboard/components/sections/UpcomingDeadlinesSection.tsx

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>
);
}