/** * RequestDetail Component * * Purpose: Display and manage detailed view of a workflow request * * Features: * - View request details and approval workflow * - Approve/Reject requests (if user is assigned approver) * - Add/Skip approvers (if user is initiator) * - Upload and view documents * - Real-time work notes chat * - Activity timeline with audit trail * - TAT/SLA tracking with alerts * - Conclusion remark generation and finalization * * Architecture: * - Custom hooks for complex logic (useRequestDetails, useRequestSocket, etc.) * - Reusable components (ApprovalStepCard, DocumentCard, SLAProgressBar) * - Error boundary for graceful error handling * - Real-time WebSocket integration */ import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { Component, ErrorInfo, ReactNode } from 'react'; // UI Components import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Textarea } from '@/components/ui/textarea'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; // Utility imports import { formatDateTime, formatDateShort } from '@/utils/dateFormatter'; import { getPriorityConfig, getStatusConfig, getActionTypeIcon } from '@/utils/requestDetailHelpers.tsx'; // Service imports import { downloadDocument, getDocumentPreviewUrl } from '@/services/workflowApi'; // Component imports import { FilePreview } from '@/components/common/FilePreview'; import { ApprovalModal } from '@/components/approval/ApprovalModal/ApprovalModal'; import { RejectionModal } from '@/components/approval/RejectionModal/RejectionModal'; import { SkipApproverModal } from '@/components/approval/SkipApproverModal'; import { AddApproverModal } from '@/components/participant/AddApproverModal'; import { AddSpectatorModal } from '@/components/participant/AddSpectatorModal'; import { ActionStatusModal } from '@/components/common/ActionStatusModal'; import { WorkNoteChat } from '@/components/workNote/WorkNoteChat/WorkNoteChat'; import { ApprovalStepCard } from '@/components/workflow/ApprovalWorkflow'; import { DocumentCard } from '@/components/workflow/DocumentUpload'; import { SLAProgressBar } from '@/components/sla/SLAProgressBar'; // Context and hooks import { useAuth } from '@/contexts/AuthContext'; import { useRequestDetails } from '@/hooks/useRequestDetails'; import { useRequestSocket } from '@/hooks/useRequestSocket'; import { useDocumentUpload } from '@/hooks/useDocumentUpload'; import { useConclusionRemark } from '@/hooks/useConclusionRemark'; import { useModalManager } from '@/hooks/useModalManager'; // Icon imports import { ArrowLeft, User, FileText, MessageSquare, CheckCircle, XCircle, Eye, TrendingUp, RefreshCw, Activity, Mail, Phone, Upload, UserPlus, ClipboardList, AlertTriangle, Loader2, AlertCircle } from 'lucide-react'; /** * Error Boundary Component * * Purpose: Catch and handle React errors gracefully * * Benefits: * - Prevents entire app crash if RequestDetail has an error * - Shows user-friendly error message * - Provides recovery options (reload or go back) * - Logs errors for debugging * * Catches: * - Runtime errors in component tree * - Rendering errors * - Lifecycle method errors */ class RequestDetailErrorBoundary extends Component< { children: ReactNode }, { hasError: boolean; error: Error | null } > { constructor(props: { children: ReactNode }) { super(props); this.state = { hasError: false, error: null }; } // Static method: Update state when error is caught static getDerivedStateFromError(error: Error) { return { hasError: true, error }; } // Lifecycle: Log error details for debugging override componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error('RequestDetail Error:', error, errorInfo); } // Render: Show error UI or normal children override render() { if (this.state.hasError) { return (

Error Loading Request

{this.state.error?.message || 'An unexpected error occurred'}

); } return this.props.children; } } /** * RequestDetailProps Interface * * Props: * @param requestId - Request number or UUID to load * @param onBack - Optional callback for back button navigation * @param dynamicRequests - Optional array of requests for fallback data */ interface RequestDetailProps { requestId: string; onBack?: () => void; dynamicRequests?: any[]; } /** * RequestDetailInner Component * * Purpose: Main component logic for request detail view * * Architecture: * - Uses custom hooks for complex logic (data fetching, socket, document upload, etc.) * - Delegates state management to specialized hooks * - Focuses on UI rendering and user interactions * - All heavy lifting is done by hooks, keeping this component clean */ function RequestDetailInner({ requestId: propRequestId, onBack, dynamicRequests = [] }: RequestDetailProps) { // Route params: Get request identifier from URL const params = useParams<{ requestId: string }>(); const requestIdentifier = params.requestId || propRequestId || ''; // URL query params: Read initial tab from URL (e.g., ?tab=worknotes) const urlParams = new URLSearchParams(window.location.search); const initialTab = urlParams.get('tab') || 'overview'; // State: Currently active tab const [activeTab, setActiveTab] = useState(initialTab); // Auth: Get current logged-in user const { user } = useAuth(); /** * Custom Hook: useRequestDetails * * Handles: * - Request data fetching and transformation * - Approval flow mapping with TAT alerts * - Spectator and participant management * - Current user's role determination (initiator, approver, spectator) * - Data refresh functionality */ const { request, apiRequest, refreshing, refreshDetails, currentApprovalLevel, isSpectator, isInitiator, existingParticipants } = useRequestDetails(requestIdentifier, dynamicRequests, user); /** * Custom Hook: useRequestSocket * * Handles: * - WebSocket connection management * - Real-time work notes updates * - TAT alerts listening * - Merged timeline of work notes and activities * - Unread work notes badge */ const { mergedMessages, unreadWorkNotes, workNoteAttachments, setWorkNoteAttachments } = useRequestSocket(requestIdentifier, apiRequest, activeTab, user); /** * Custom Hook: useDocumentUpload * * Handles: * - Document upload functionality * - File input triggering * - Upload loading state * - Document preview modal */ const { uploadingDocument, triggerFileInput, previewDocument, setPreviewDocument, documentPolicy, documentError, setDocumentError } = useDocumentUpload(apiRequest, refreshDetails); /** * Custom Hook: useModalManager * * Handles: * - All modal visibility states * - Approve/Reject/Skip actions * - Add approver/spectator actions * - Action status feedback */ const { showApproveModal, setShowApproveModal, showRejectModal, setShowRejectModal, showAddApproverModal, setShowAddApproverModal, showAddSpectatorModal, setShowAddSpectatorModal, showSkipApproverModal, setShowSkipApproverModal, showActionStatusModal, setShowActionStatusModal, skipApproverData, setSkipApproverData, actionStatus, setActionStatus, handleApproveConfirm, handleRejectConfirm, handleAddApprover, handleSkipApprover, handleAddSpectator } = useModalManager(requestIdentifier, currentApprovalLevel, refreshDetails); /** * Custom Hook: useConclusionRemark * * Handles: * - AI-generated conclusion fetching * - Conclusion generation * - Conclusion finalization and request closure * - Navigation after closure */ const { conclusionRemark, setConclusionRemark, conclusionLoading, conclusionSubmitting, aiGenerated, handleGenerateConclusion, handleFinalizeConclusion } = useConclusionRemark( request, requestIdentifier, isInitiator, refreshDetails, onBack, setActionStatus, setShowActionStatusModal ); /** * Effect: Auto-switch tab when URL query parameter changes * * Use Case: When user clicks notification, URL includes ?tab=worknotes * This effect automatically switches to the specified tab * * Trigger: When requestIdentifier changes (navigating to different request) */ useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const tabParam = urlParams.get('tab'); if (tabParam) { console.log('[RequestDetail] Auto-switching to tab:', tabParam); setActiveTab(tabParam); } }, [requestIdentifier]); /** * Handler: handleRefresh * * Purpose: Trigger manual refresh of request data * Delegates to useRequestDetails hook's refreshDetails function */ const handleRefresh = () => { refreshDetails(); }; /** * Computed: Get display configuration for priority and status badges * Uses helper functions from requestDetailHelpers utility */ const priorityConfig = getPriorityConfig(request?.priority || 'standard'); const statusConfig = getStatusConfig(request?.status || 'pending'); /** * Computed: Determine if request needs conclusion from initiator * TRUE when: Request is approved AND current user is the initiator * Purpose: Show conclusion remark form to close the request */ const needsClosure = request?.status === 'approved' && isInitiator; /** * Early return: Show loading state while request data is being fetched */ if (!request && !apiRequest) { return (

Loading request details...

); } /** * Early return: Show error state if request not found */ if (!request) { return (

Request Not Found

The request you're looking for doesn't exist.

); } /** * Main Render: Request detail UI with tabs * * Layout structure: * - Header: Request ID, title, priority, status, refresh button * - SLA Progress Bar: Overall request SLA/TAT tracking * - Tabs: Overview, Workflow, Documents, Activity, Work Notes * - Sidebar: Quick actions (approve, reject, add participants) * - Modals: All action modals rendered at bottom */ return ( <>
{/* Header Section: Request ID, title, badges, refresh button */}
{/* Top Header */}
{/* Back Button: Navigate to previous page */}
{/* File Icon */}
{/* Request ID/Number */}

{request.id || 'N/A'}

{/* Priority and Status Badges */}
{priorityConfig.label} {statusConfig.label}
{/* Refresh Button: Manually reload request data */}
{/* Request Title */}

{request.title}

{/* SLA Progress Section: Shows overall request SLA/TAT from backend */}
{/* Tabs: Overview, Workflow, Documents, Activity, Work Notes */} Overview Workflow Docs Activity Work Notes {/* Unread Work Notes Badge */} {unreadWorkNotes > 0 && ( {unreadWorkNotes > 9 ? '9+' : unreadWorkNotes} )} {/* Main Layout: Full width for Work Notes, Grid with sidebar for other tabs */}
{/* Left Column: Tab content (2/3 width normally, full width for work notes) */}
{/* Overview Tab: Request initiator, details, and conclusion */}
{/* Request Initiator Card */} Request Initiator
{request.initiator?.avatar || 'U'}

{request.initiator?.name || 'N/A'}

{request.initiator?.role || 'N/A'}

{request.initiator?.department || 'N/A'}

{request.initiator?.email || 'N/A'}
{request.initiator?.phone || 'N/A'}
{/* Request Details Card */} Request Details

{request.description}

{/* Additional Details: Category, Subcategory (if applicable) */} {(request.category || request.subcategory) && (
{request.category && (

{request.category}

)} {request.subcategory && (

{request.subcategory}

)}
)} {/* Amount (if applicable) */} {request.amount && (

{request.amount}

)} {/* Timestamps: Created and Last Updated */}

{formatDateTime(request.createdAt)}

{formatDateTime(request.updatedAt)}

{/* Claim Management Details: Show only for claim management requests */} {request.claimDetails && ( Claim Management Details

{request.claimDetails.activityName || 'N/A'}

{request.claimDetails.activityType || 'N/A'}

{request.claimDetails.location || 'N/A'}

{request.claimDetails.activityDate ? formatDateShort(request.claimDetails.activityDate) : 'N/A'}

{request.claimDetails.dealerCode || 'N/A'}

{request.claimDetails.dealerName || 'N/A'}

{request.claimDetails.requestDescription && (

{request.claimDetails.requestDescription}

)}
)} {/* Read-Only Conclusion Remark: Shows for closed requests */} {request.status === 'closed' && request.conclusionRemark && ( Conclusion Remark Final summary of this closed request

{request.conclusionRemark}

{request.closureDate && (
Request closed on {formatDateTime(request.closureDate)} By {request.initiator?.name || 'Initiator'}
)}
)} {/* Conclusion Remark Section: Shows when request is approved (initiator finalizes) */} {needsClosure && (
Conclusion Remark - Final Step All approvals are complete. Please review and finalize the conclusion to close this request.
{/* AI Generation Button */}
{conclusionLoading ? (

Preparing conclusion remark...

) : (
{/* AI Generated Badge */} {aiGenerated && ( ✓ System-generated suggestion (editable) )}
{/* Conclusion Textarea */}