From 8c2aa60195ed615a6d4fea3cd3abf9757b7ff462 Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Sat, 29 Nov 2025 16:17:14 +0530 Subject: [PATCH] resume pause and sla progress bar in detail screen modified --- .../sla/SLAProgressBar/SLAProgressBar.tsx | 68 +++++++++--- .../CreateRequest/ApprovalWorkflowStep.tsx | 105 +++++++++++++++++- src/pages/OpenRequests/OpenRequests.tsx | 92 +++++++++------ 3 files changed, 214 insertions(+), 51 deletions(-) diff --git a/src/components/sla/SLAProgressBar/SLAProgressBar.tsx b/src/components/sla/SLAProgressBar/SLAProgressBar.tsx index 0af90a5..e4c2930 100644 --- a/src/components/sla/SLAProgressBar/SLAProgressBar.tsx +++ b/src/components/sla/SLAProgressBar/SLAProgressBar.tsx @@ -5,7 +5,7 @@ import { formatHoursMinutes } from '@/utils/slaTracker'; import { formatDateDDMMYYYY } from '@/utils/dateFormatter'; export interface SLAData { - status: 'normal' | 'approaching' | 'critical' | 'breached'; + status: 'on_track' | 'normal' | 'approaching' | 'critical' | 'breached'; percentageUsed: number; elapsedText: string; elapsedHours: number; @@ -42,6 +42,47 @@ export function SLAProgressBar({ ); } + // Use percentage-based colors to match approver SLA tracker + // Green: 0-50%, Amber: 50-75%, Orange: 75-100%, Red: 100%+ (breached) + const percentageUsed = sla.percentageUsed || 0; + const rawStatus = sla.status || 'on_track'; + + // Determine colors based on percentage (matching ApprovalStepCard logic) + const getStatusColors = () => { + if (percentageUsed >= 100) { + return { + badge: 'bg-red-600 text-white animate-pulse', + progress: 'bg-red-600', + text: 'text-red-600' + }; + } else if (percentageUsed >= 75) { + return { + badge: 'bg-orange-500 text-white', + progress: 'bg-orange-500', + text: 'text-orange-600' + }; + } else if (percentageUsed >= 50) { + return { + badge: 'bg-amber-500 text-white', + progress: 'bg-amber-500', + text: 'text-amber-600' + }; + } else { + return { + badge: 'bg-green-600 text-white', + progress: 'bg-green-600', + text: 'text-gray-700' + }; + } + }; + + const colors = getStatusColors(); + + // Normalize status for warning messages (still use status for text warnings) + const normalizedStatus = (rawStatus === 'on_track' || rawStatus === 'normal') + ? 'normal' + : rawStatus; + return (
@@ -50,12 +91,7 @@ export function SLAProgressBar({ SLA Progress
{sla.percentageUsed || 0}% elapsed @@ -64,12 +100,8 @@ export function SLAProgressBar({ div]:bg-red-600' : - sla.status === 'critical' ? '[&>div]:bg-orange-600' : - sla.status === 'approaching' ? '[&>div]:bg-yellow-600' : - '[&>div]:bg-green-600' - }`} + className="h-3 mb-2" + indicatorClassName={colors.progress} data-testid={`${testId}-bar`} /> @@ -79,13 +111,13 @@ export function SLAProgressBar({ - {sla.remainingText || formatHoursMinutes(sla.remainingHours || 0)} remaining + {formatHoursMinutes(sla.remainingHours || 0)} remaining
@@ -95,13 +127,13 @@ export function SLAProgressBar({

)} - {sla.status === 'critical' && ( + {normalizedStatus === 'critical' && (

Approaching Deadline

)} - {sla.status === 'breached' && ( + {normalizedStatus === 'breached' && (

URGENT - Deadline Passed diff --git a/src/components/workflow/CreateRequest/ApprovalWorkflowStep.tsx b/src/components/workflow/CreateRequest/ApprovalWorkflowStep.tsx index 8247d32..172ed03 100644 --- a/src/components/workflow/CreateRequest/ApprovalWorkflowStep.tsx +++ b/src/components/workflow/CreateRequest/ApprovalWorkflowStep.tsx @@ -6,7 +6,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Badge } from '@/components/ui/badge'; -import { Users, Settings, Shield, User, CheckCircle, Minus, Plus } from 'lucide-react'; +import { Users, Settings, Shield, User, CheckCircle, Minus, Plus, Info, Clock } from 'lucide-react'; import { FormData } from '@/hooks/useCreateRequestForm'; import { useMultiUserSearch } from '@/hooks/useUserSearch'; import { ensureUserExists } from '@/services/userApi'; @@ -409,6 +409,109 @@ export function ApprovalWorkflowStep({ })} + + {/* TAT Summary Section */} +

+ {/* Approval Flow Summary */} +
+
+ +
+

Approval Flow Summary

+

+ Your request will follow this sequence: You (Initiator) → {Array.from({ length: formData.approverCount || 1 }, (_, i) => `Level ${i + 1} Approver`).join(' → ')}. The final approver can close the request. +

+
+
+
+ + {/* TAT Summary */} +
+
+ +
+
+

TAT Summary

+
+ {(() => { + // Calculate total calendar days (for display) + // Days: count as calendar days + // Hours: convert to calendar days (hours / 24) + const totalCalendarDays = formData.approvers?.reduce((sum: number, a: any) => { + const tat = Number(a.tat || 0); + const tatType = a.tatType || 'hours'; + if (tatType === 'days') { + return sum + tat; // Calendar days + } else { + return sum + (tat / 24); // Convert hours to calendar days + } + }, 0) || 0; + const displayDays = Math.ceil(totalCalendarDays); + return ( + <> +
{displayDays} {displayDays === 1 ? 'Day' : 'Days'}
+
Total Duration
+ + ); + })()} +
+
+
+
+ {formData.approvers?.map((approver: any, idx: number) => { + const tat = Number(approver.tat || 0); + const tatType = approver.tatType || 'hours'; + // Convert days to hours: 1 day = 24 hours + const hours = tatType === 'days' ? tat * 24 : tat; + if (!tat) return null; + return ( +
+
+ Level {idx + 1} + {hours} {hours === 1 ? 'hour' : 'hours'} +
+
+ ); + })} +
+ {(() => { + // Convert all TAT to hours first + // Days: 1 day = 24 hours + // Hours: already in hours + const totalHours = formData.approvers?.reduce((sum: number, a: any) => { + const tat = Number(a.tat || 0); + const tatType = a.tatType || 'hours'; + if (tatType === 'days') { + // 1 day = 24 hours + return sum + (tat * 24); + } else { + return sum + tat; + } + }, 0) || 0; + // Convert total hours to working days (8 hours per working day) + const workingDays = Math.ceil(totalHours / 8); + if (totalHours === 0) return null; + return ( +
+
+
+
{totalHours}{totalHours === 1 ? 'h' : 'h'}
+
Total Hours
+
+
+
{workingDays}
+
Working Days*
+
+
+

*Based on 8-hour working days

+
+ ); + })()} +
+
+
+
+
); diff --git a/src/pages/OpenRequests/OpenRequests.tsx b/src/pages/OpenRequests/OpenRequests.tsx index ccdb86f..ce577ac 100644 --- a/src/pages/OpenRequests/OpenRequests.tsx +++ b/src/pages/OpenRequests/OpenRequests.tsx @@ -480,40 +480,68 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) { {/* SLA Display - Compact Version */} - {request.currentLevelSLA && ( -
-
-
- - TAT: {request.currentLevelSLA.percentageUsed}% -
-
- {request.currentLevelSLA.elapsedText} - - {request.currentLevelSLA.remainingText} left - + {request.currentLevelSLA && (() => { + // Use percentage-based colors to match approver SLA tracker + // Green: 0-50%, Amber: 50-75%, Orange: 75-100%, Red: 100%+ (breached) + const percentUsed = request.currentLevelSLA.percentageUsed || 0; + + const getSLAColors = () => { + if (percentUsed >= 100) { + return { + bg: 'bg-red-50 border border-red-200', + progress: 'bg-red-600', + text: 'text-red-600' + }; + } else if (percentUsed >= 75) { + return { + bg: 'bg-orange-50 border border-orange-200', + progress: 'bg-orange-500', + text: 'text-orange-600' + }; + } else if (percentUsed >= 50) { + return { + bg: 'bg-amber-50 border border-amber-200', + progress: 'bg-amber-500', + text: 'text-amber-600' + }; + } else { + return { + bg: 'bg-green-50 border border-green-200', + progress: 'bg-green-600', + text: 'text-gray-700' + }; + } + }; + + const colors = getSLAColors(); + + return ( +
+
+
+ + TAT: {percentUsed}% +
+
+ {request.currentLevelSLA.elapsedText} + = 100 ? 'text-red-600' : + percentUsed >= 75 ? 'text-orange-600' : + percentUsed >= 50 ? 'text-amber-600' : + 'text-gray-700' + }`}> + {request.currentLevelSLA.remainingText} left + +
+
- div]:bg-red-600' : - request.currentLevelSLA.status === 'critical' ? '[&>div]:bg-orange-600' : - request.currentLevelSLA.status === 'approaching' ? '[&>div]:bg-yellow-600' : - '[&>div]:bg-green-600' - }`} - /> -
- )} + ); + })()} {/* Metadata Row */}