/** * Quick Actions Sidebar Component */ import { useEffect, useMemo, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { UserPlus, Eye, CheckCircle, XCircle, Share2, Pause, Play, AlertCircle } from 'lucide-react'; import { getSharedRecipients, type SharedRecipient } from '@/services/summaryApi'; import { useAuth } from '@/contexts/AuthContext'; import notificationApi, { type Notification } from '@/services/notificationApi'; import { ProcessDetailsCard } from './claim-cards'; import { isClaimManagementRequest } from '@/utils/claimRequestUtils'; import { determineUserRole, getRoleBasedVisibility, mapToClaimManagementRequest } from '@/utils/claimDataMapper'; interface QuickActionsSidebarProps { request: any; isInitiator: boolean; isSpectator: boolean; currentApprovalLevel: any; onAddApprover: () => void; onAddSpectator: () => void; onApprove: () => void; onReject: () => void; onPause?: () => void; onResume?: () => void; onRetrigger?: () => void; summaryId?: string | null; refreshTrigger?: number; // Trigger to refresh shared recipients list pausedByUserId?: string; // User ID of the approver who paused (kept for backwards compatibility) currentUserId?: string; // Current user's ID (kept for backwards compatibility) apiRequest?: any; onEditClaimAmount?: () => void; } export function QuickActionsSidebar({ request, isInitiator, isSpectator, currentApprovalLevel, onAddApprover, onAddSpectator, onApprove, onReject, onPause, onResume, onRetrigger, summaryId, refreshTrigger, pausedByUserId: pausedByUserIdProp, currentUserId: currentUserIdProp, apiRequest, onEditClaimAmount, }: QuickActionsSidebarProps) { const { user } = useAuth(); const [sharedRecipients, setSharedRecipients] = useState([]); const [loadingRecipients, setLoadingRecipients] = useState(false); const [hasRetriggerNotification, setHasRetriggerNotification] = useState(false); const isClosed = request?.status === 'closed'; const isPaused = request?.pauseInfo?.isPaused || false; const pausedByUserId = pausedByUserIdProp || request?.pauseInfo?.pausedBy?.userId; const currentUserId = currentUserIdProp || (user as any)?.userId || ''; // Both approver AND initiator can pause (when not already paused and not closed) const canPause = !isPaused && !isClosed && (currentApprovalLevel || isInitiator); // Resume: Can be done by the person who paused OR by both initiator and approver const canResume = isPaused && onResume && (currentApprovalLevel || isInitiator); // Retrigger: Only for initiator when approver paused (initiator asks approver to resume) const canRetrigger = isPaused && isInitiator && pausedByUserId && pausedByUserId !== currentUserId && onRetrigger; // Check for retrigger notification (initiator requested resume) // ONLY check when: 1) Request is paused, 2) Current user is an approver // This avoids unnecessary API calls for non-paused requests or initiators useEffect(() => { // Skip check if request is not paused or user is not an approver if (!isPaused || !currentApprovalLevel || !request?.requestId) { setHasRetriggerNotification(false); return; } const checkRetriggerNotification = async () => { try { const response = await notificationApi.list({ page: 1, limit: 50, unreadOnly: true }); // Only unread const notifications: Notification[] = response.data?.notifications || []; // Check if there's an UNREAD pause_retrigger_request notification for this request const hasRetrigger = notifications.some( (notif: Notification) => notif.requestId === request.requestId && notif.notificationType === 'pause_retrigger_request' ); setHasRetriggerNotification(hasRetrigger); } catch (error) { console.error('Failed to check retrigger notifications:', error); setHasRetriggerNotification(false); } }; checkRetriggerNotification(); }, [isPaused, currentApprovalLevel, request?.requestId, refreshTrigger]); // Only when paused state or approver status changes // Fetch shared recipients when request is closed and summaryId is available useEffect(() => { const fetchSharedRecipients = async () => { if (!isClosed || !summaryId || !isInitiator) { setSharedRecipients([]); return; } try { setLoadingRecipients(true); const recipients = await getSharedRecipients(summaryId); setSharedRecipients(recipients); } catch (error) { console.error('Failed to fetch shared recipients:', error); setSharedRecipients([]); } finally { setLoadingRecipients(false); } }; fetchSharedRecipients(); }, [isClosed, summaryId, isInitiator, refreshTrigger]); // Claim details for sidebar (only for claim management requests) const claimSidebarData = useMemo(() => { if (!apiRequest || !isClaimManagementRequest(apiRequest)) return null; const claimRequest = mapToClaimManagementRequest(apiRequest, currentUserId); if (!claimRequest) return null; const userRole = determineUserRole(apiRequest, currentUserId); const visibility = getRoleBasedVisibility(userRole); return { claimRequest, visibility }; }, [apiRequest, currentUserId]); return (
{/* Quick Actions Card - Hide entire card for spectators and closed requests */} {!isSpectator && request.status !== 'closed' && ( Quick Actions {/* Add Approver */} {isInitiator && request.status !== 'closed' && ( )} {/* Add Spectator */} {request.status !== 'closed' && ( )} {/* Pause/Resume Button */} {canPause && onPause && ( )} {canResume && ( )} {canRetrigger && ( )} {/* Approve/Reject Buttons */}
{currentApprovalLevel && !isPaused && ( <> )} {isPaused && (
{/* Different messages based on who paused, who is viewing, and if retrigger was sent */} {pausedByUserId === currentUserId ? ( // User viewing is the one who paused <>

{hasRetriggerNotification && } {hasRetriggerNotification ? 'Initiator has requested you to resume' : 'You paused this workflow'}

{hasRetriggerNotification ? 'Please review and resume if appropriate' : 'Click "Resume Workflow" to continue'}

) : currentApprovalLevel && pausedByUserId !== currentUserId && hasRetriggerNotification ? ( // Approver viewing, and initiator sent retrigger <>

Initiator has requested resume

Please review and resume if appropriate

) : currentApprovalLevel && pausedByUserId !== currentUserId ? ( // Approver viewing but someone else paused (initiator or another approver) <>

Workflow is paused

You can resume to continue approval

) : isInitiator && pausedByUserId && pausedByUserId !== currentUserId ? ( // Initiator viewing, approver paused <>

Approver has paused this workflow

{canRetrigger ? 'Click "Request Resume" to notify approver' : 'Resume request sent - Waiting for approver'}

) : ( // Default message <>

Workflow is paused

Actions are disabled until resumed

)}
)}
)} {/* Spectators Card */} Spectators {request.spectators && request.spectators.length > 0 ? ( request.spectators.map((spectator: any, index: number) => (
{spectator.avatar}

{spectator.name}

{spectator.role}

)) ) : (

No spectators added

)}
{/* Shared Recipients Card - Only for closed requests */} {isClosed && isInitiator && ( Summary Shared With {loadingRecipients ? (

Loading...

) : sharedRecipients.length > 0 ? ( sharedRecipients.map((recipient, index) => { const avatar = (recipient.displayName || 'NA') .split(' ') .map((s: string) => s[0]) .join('') .slice(0, 2) .toUpperCase(); return (
{avatar}

{recipient.displayName}

{recipient.email}

{recipient.isRead && (

Viewed

)}
); }) ) : (

Summary not shared yet

)}
)} {/* Process details anchored at the bottom of the action sidebar for claim workflows */} {claimSidebarData && ( )}
); }