/**
* 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)}
);
}