From fe441c8b76f723b573077b30157b0f180615e22a Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Thu, 27 Nov 2025 18:36:55 +0530 Subject: [PATCH] pause feature added and also all request as normal user for admin --- .../ApprovalWorkflow/ApprovalStepCard.tsx | 11 +- src/components/workflow/PauseModal.tsx | 179 +++++++++++ .../workflow/RetriggerPauseModal.tsx | 85 ++++++ src/contexts/AuthContext.tsx | 111 +++++-- src/hooks/useRequestDetails.ts | 27 +- src/main.tsx | 10 +- src/pages/Dashboard/Dashboard.tsx | 18 +- .../Dashboard/components/DashboardHero.tsx | 30 +- .../components/sections/AdminKPICards.tsx | 18 +- src/pages/Dashboard/redux/dashboardSlice.ts | 35 +++ src/pages/MyRequests/MyRequests.tsx | 4 + .../components/MyRequestsFilters.tsx | 1 + .../MyRequests/components/MyRequestsStats.tsx | 16 +- .../MyRequests/components/RequestCard.tsx | 12 +- .../MyRequests/hooks/useMyRequestsStats.ts | 1 + .../MyRequests/types/myRequests.types.ts | 7 + src/pages/RequestDetail/RequestDetail.tsx | 68 +++++ .../components/QuickActionsSidebar.tsx | 63 +++- .../components/tabs/OverviewTab.tsx | 105 ++++++- .../components/tabs/WorkflowTab.tsx | 6 + src/pages/Requests/Requests.tsx | 89 +++--- src/pages/Requests/UserAllRequests.tsx | 234 ++++++++------- src/pages/Requests/components/RequestCard.tsx | 12 +- .../Requests/components/RequestsHeader.tsx | 46 ++- .../Requests/components/RequestsStats.tsx | 16 +- .../Requests/services/requestsService.ts | 11 +- .../Requests/services/userRequestsService.ts | 277 +++++------------- src/pages/Requests/types/requests.types.ts | 2 + .../Requests/utils/requestCalculations.ts | 8 + src/pages/Requests/utils/requestFilters.ts | 16 +- src/redux/store.ts | 2 + src/services/authApi.ts | 62 +++- src/services/dashboard.service.ts | 8 +- src/services/workflowApi.ts | 73 ++++- src/types/index.ts | 22 +- src/utils/tokenManager.ts | 242 +++++++-------- 36 files changed, 1334 insertions(+), 593 deletions(-) create mode 100644 src/components/workflow/PauseModal.tsx create mode 100644 src/components/workflow/RetriggerPauseModal.tsx create mode 100644 src/pages/Dashboard/redux/dashboardSlice.ts diff --git a/src/components/workflow/ApprovalWorkflow/ApprovalStepCard.tsx b/src/components/workflow/ApprovalWorkflow/ApprovalStepCard.tsx index 8c9f1b6..7fce1be 100644 --- a/src/components/workflow/ApprovalWorkflow/ApprovalStepCard.tsx +++ b/src/components/workflow/ApprovalWorkflow/ApprovalStepCard.tsx @@ -39,6 +39,7 @@ interface ApprovalStepCardProps { approval?: any; // Raw approval data from backend isCurrentUser?: boolean; isInitiator?: boolean; + isCurrentLevel?: boolean; // Whether this step is the current active level onSkipApprover?: (data: { levelId: string; approverName: string; levelNumber: number }) => void; onRefresh?: () => void | Promise; // Optional callback to refresh data testId?: string; @@ -66,6 +67,8 @@ const getStepIcon = (status: string, isSkipped?: boolean) => { return ; case 'rejected': return ; + case 'paused': + return ; case 'pending': case 'in-review': return ; @@ -82,6 +85,7 @@ export function ApprovalStepCard({ approval, isCurrentUser = false, isInitiator = false, + isCurrentLevel = false, onSkipApprover, onRefresh, testId = 'approval-step' @@ -105,6 +109,7 @@ export function ApprovalStepCard({ const isCompleted = step.status === 'approved'; const isRejected = step.status === 'rejected'; const isWaiting = step.status === 'waiting'; + const isPaused = step.status === 'paused'; const tatHours = Number(step.tatHours || 0); const actualHours = step.actualHours ?? 0; @@ -180,6 +185,7 @@ export function ApprovalStepCard({
)} - {/* Active Approver - Show Real-time Progress from Backend */} - {isActive && approval?.sla && ( + {/* Active Approver (including paused) - Show Real-time Progress from Backend */} + {/* Only show SLA for the current level step, not future levels */} + {isCurrentLevel && (isActive || isPaused) && approval?.sla && (
Due by: diff --git a/src/components/workflow/PauseModal.tsx b/src/components/workflow/PauseModal.tsx new file mode 100644 index 0000000..50182e2 --- /dev/null +++ b/src/components/workflow/PauseModal.tsx @@ -0,0 +1,179 @@ +import { useState, useEffect } from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Label } from '@/components/ui/label'; +import { Textarea } from '@/components/ui/textarea'; +import { Input } from '@/components/ui/input'; +import { Loader2, Pause } from 'lucide-react'; +import { toast } from 'sonner'; +import { pauseWorkflow } from '@/services/workflowApi'; +import dayjs from 'dayjs'; + +interface PauseModalProps { + isOpen: boolean; + onClose: () => void; + requestId: string; + levelId: string | null; + onSuccess?: () => void; +} + +export function PauseModal({ isOpen, onClose, requestId, levelId, onSuccess }: PauseModalProps) { + const [reason, setReason] = useState(''); + const [resumeDate, setResumeDate] = useState(''); + const [submitting, setSubmitting] = useState(false); + + // Set default resume date to 1 month from now + const getDefaultResumeDate = () => { + const maxDate = dayjs().add(1, 'month').format('YYYY-MM-DD'); + return maxDate; + }; + + const getMaxResumeDate = () => { + return dayjs().add(1, 'month').format('YYYY-MM-DD'); + }; + + const getMinResumeDate = () => { + return dayjs().add(1, 'day').format('YYYY-MM-DD'); // At least 1 day from now + }; + + // Initialize resume date when modal opens + useEffect(() => { + if (isOpen && !resumeDate) { + setResumeDate(getDefaultResumeDate()); + } + }, [isOpen]); + + const handleSubmit = async () => { + if (!reason.trim()) { + toast.error('Please provide a reason for pausing'); + return; + } + + if (!resumeDate) { + toast.error('Please select a resume date'); + return; + } + + const selectedDate = dayjs(resumeDate); + const maxDate = dayjs().add(1, 'month'); + const minDate = dayjs().add(1, 'day'); + + if (selectedDate.isAfter(maxDate)) { + toast.error('Resume date cannot be more than 1 month from now'); + return; + } + + if (selectedDate.isBefore(minDate, 'day')) { + toast.error('Resume date must be at least 1 day from now'); + return; + } + + try { + setSubmitting(true); + await pauseWorkflow(requestId, levelId, reason.trim(), selectedDate.toDate()); + toast.success('Workflow paused successfully'); + setReason(''); + setResumeDate(getDefaultResumeDate()); + onSuccess?.(); + onClose(); + } catch (error: any) { + console.error('Failed to pause workflow:', error); + toast.error(error?.response?.data?.error || error?.response?.data?.message || 'Failed to pause workflow'); + } finally { + setSubmitting(false); + } + }; + + const handleClose = () => { + if (!submitting) { + setReason(''); + setResumeDate(getDefaultResumeDate()); + onClose(); + } + }; + + return ( + + + + + + Pause Workflow + + + +
+
+

+ Note: Pausing will temporarily halt TAT calculations and notifications. The workflow will automatically resume on the selected date. +

+
+ +
+ +