/** * Individual Request Card Component */ import { motion } from 'framer-motion'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent } from '@/components/ui/card'; import { User, ArrowRight, TrendingUp, Clock, Pause } from 'lucide-react'; import { getPriorityConfig, getStatusConfig } from '../utils/configMappers'; import type { ConvertedRequest } from '../types/requests.types'; import { formatDateDDMMYYYY } from '@/utils/dateFormatter'; /** * Strip HTML tags and convert to plain text for card preview */ const stripHtmlTags = (html: string): string => { if (!html) return ''; // 1. Replace block-level tags with a space to avoid merging words (e.g.
-> " ") // This preserves readability for the card preview let text = html.replace(/<(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hr|li|main|nav|noscript|ol|p|pre|section|table|tfoot|ul|video)[^>]*>/gi, ' '); // 2. Replace
with space text = text.replace(//gi, ' '); // 3. Strip all other tags text = text.replace(/<[^>]*>/g, ''); // 4. Clean up extra whitespace text = text.replace(/\s+/g, ' ').trim(); // 5. Basic HTML entity decoding for common characters text = text .replace(/ /g, ' ') .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, "'"); return text; }; interface RequestCardProps { request: ConvertedRequest; index: number; onViewRequest: (requestId: string, requestTitle?: string, status?: string) => void; } export function RequestCard({ request, index, onViewRequest }: RequestCardProps) { const statusConfig = getStatusConfig(request.status); const priorityConfig = getPriorityConfig(request.priority); const StatusIcon = statusConfig.icon; const PriorityIcon = priorityConfig.icon; return ( onViewRequest(request.id, request.title, request.status)} data-testid={`request-card-${request.id}`} >
{/* Header with Title and Status Badges */}

{request.title}

{request.status} {((request as any).pauseInfo?.isPaused || (request as any).isPaused) && ( Paused )} {request.priority} {/* Template Type Badge */} {(() => { const templateType = request?.templateType || (request as any)?.template_type || ''; const templateTypeUpper = templateType?.toUpperCase() || ''; // Direct mapping from templateType let templateLabel = 'Non-Templatized'; let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200'; if (templateTypeUpper === 'DEALER CLAIM') { templateLabel = 'Dealer Claim'; templateColor = 'bg-blue-100 !text-blue-700 border-blue-200'; } else if (templateTypeUpper === 'TEMPLATE') { templateLabel = 'Template'; } return ( {templateLabel} ); })()} {request.department && ( {request.department} )}

{stripHtmlTags(request.description || '') || 'No description provided'}

ID: {request.displayId || request.id} Submitted: {formatDateDDMMYYYY(request.submittedDate)}
{/* Current Approver and Level Info */}
Current Approver: {request.currentApprover}
Approval Level: {request.approverLevel}
Submitted: {formatDateDDMMYYYY(request.submittedDate)}
); }