/* * File: PatientCard.tsx * Description: Patient card component for displaying DICOM medical case information * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import React from 'react'; import { View, Text, TouchableOpacity, StyleSheet, } from 'react-native'; import { theme } from '../../../theme/theme'; import Icon from 'react-native-vector-icons/Feather'; import { PatientData } from '../redux/patientCareSlice'; // ============================================================================ // INTERFACES // ============================================================================ interface PatientCardProps { patient: PatientData; onPress: () => void; onEmergencyPress?: () => void; } // ============================================================================ // PATIENT CARD COMPONENT // ============================================================================ /** * PatientCard Component * * Purpose: Display DICOM medical case information in a card format * * Features: * - Patient basic information from DICOM data * - Modality and institution information * - Processing status with color coding * - Series information * - Time since processed * - Emergency alert for critical cases * - Modern ER-focused design */ const PatientCard: React.FC = ({ patient, onPress, onEmergencyPress, }) => { // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ /** * Get Status Color Configuration * * Purpose: Get color and icon based on processing status * * @param status - Processing status * @returns Color configuration object */ const getStatusConfig = (status: string) => { switch (status.toLowerCase()) { case 'processed': return { color: theme.colors.success, icon: 'check-circle', bgColor: '#F0FFF4' }; case 'pending': return { color: theme.colors.warning, icon: 'clock', bgColor: '#FFF8E1' }; case 'error': return { color: theme.colors.error, icon: 'alert-triangle', bgColor: '#FFF5F5' }; default: return { color: theme.colors.primary, icon: 'info', bgColor: theme.colors.background }; } }; /** * Get Modality Color * * Purpose: Get color based on imaging modality * * @param modality - Imaging modality * @returns Color code */ const getModalityColor = (modality: string) => { switch (modality.toUpperCase()) { case 'CT': return '#4A90E2'; case 'MR': return '#7B68EE'; case 'DX': return '#50C878'; case 'DICOM': return '#FF6B6B'; default: return theme.colors.textSecondary; } }; /** * Format Date * * Purpose: Format date string to readable format * * @param dateString - ISO date string * @returns Formatted date string */ const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }); }; /** * Get Time Since Processed * * Purpose: Get human-readable time since last processed * * @param dateString - ISO date string * @returns Formatted time string */ const getTimeSinceProcessed = (dateString: string) => { const now = new Date(); const processed = new Date(dateString); const diffInMinutes = Math.floor((now.getTime() - processed.getTime()) / (1000 * 60)); if (diffInMinutes < 1) return 'Just now'; if (diffInMinutes < 60) return `${diffInMinutes}m ago`; if (diffInMinutes < 1440) return `${Math.floor(diffInMinutes / 60)}h ago`; return `${Math.floor(diffInMinutes / 1440)}d ago`; }; // ============================================================================ // DATA EXTRACTION // ============================================================================ const patientInfo = patient.patient_info; const seriesCount = patient.series_summary.length; const statusConfig = getStatusConfig(patientInfo.status); const isCritical = patientInfo.report_status === 'Critical' || patientInfo.status === 'Error'; // ============================================================================ // RENDER HELPERS // ============================================================================ /** * Render Status Badge * * Purpose: Render processing status indicator badge */ const renderStatusBadge = () => ( {patientInfo.status} ); /** * Render Emergency Button * * Purpose: Render emergency alert button for critical cases */ const renderEmergencyButton = () => { if (!isCritical) { return null; } return ( ALERT ); }; // ============================================================================ // MAIN RENDER // ============================================================================ return ( {/* Header Section */} {patientInfo.name || 'Unknown Patient'} ID: {patient.patid} • {patientInfo.age || 'N/A'}y • {patientInfo.sex || 'N/A'} {renderStatusBadge()} {renderEmergencyButton()} {/* Medical Information Section */} Modality {patientInfo.modality || 'N/A'} Files {patient.total_files_processed} Report {patientInfo.report_status || 'Pending'} {/* Institution */} {patientInfo.institution || 'Unknown Institution'} {/* Series Information */} Series Information {seriesCount} Series Available • {patientInfo.frame_count} Total Frames {/* Footer */} {formatDate(patientInfo.date)} {getTimeSinceProcessed(patient.last_processed_at)} Case #{patient.patid} ); }; // ============================================================================ // STYLES // ============================================================================ const styles = StyleSheet.create({ container: { backgroundColor: theme.colors.background, borderRadius: 12, padding: theme.spacing.md, marginHorizontal: theme.spacing.md, marginVertical: theme.spacing.xs, shadowColor: theme.colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, borderWidth: 1, borderColor: theme.colors.border, borderLeftWidth: 4, }, containerCritical: { borderColor: theme.colors.error, borderWidth: 2, backgroundColor: '#FFF5F5', }, // Header Section header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: theme.spacing.sm, }, headerLeft: { flex: 1, marginRight: theme.spacing.sm, }, headerRight: { flexDirection: 'row', alignItems: 'center', }, patientName: { fontSize: 18, fontWeight: 'bold', color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, patientInfo: { fontSize: 14, color: theme.colors.textSecondary, marginTop: 2, fontFamily: theme.typography.fontFamily.regular, }, // Status Badge statusBadge: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, marginRight: theme.spacing.xs, borderWidth: 1, }, statusText: { fontSize: 10, fontWeight: 'bold', marginLeft: 4, textTransform: 'uppercase', }, // Emergency Button emergencyButton: { flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.error, paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, }, emergencyButtonText: { fontSize: 10, fontWeight: 'bold', color: theme.colors.background, marginLeft: 4, }, // Medical Section medicalSection: { marginBottom: theme.spacing.sm, paddingBottom: theme.spacing.sm, borderBottomWidth: 1, borderBottomColor: theme.colors.border, }, infoRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: theme.spacing.sm, }, infoItem: { flex: 1, alignItems: 'center', }, infoLabel: { fontSize: 10, color: theme.colors.textMuted, marginBottom: 2, textTransform: 'uppercase', fontWeight: '500', }, infoValue: { fontSize: 14, fontWeight: '600', color: theme.colors.textPrimary, textAlign: 'center', }, modalityText: { fontWeight: 'bold', }, // Institution Row institutionRow: { flexDirection: 'row', alignItems: 'center', }, institutionText: { fontSize: 14, color: theme.colors.textSecondary, marginLeft: 6, flex: 1, fontFamily: theme.typography.fontFamily.regular, }, // Series Section seriesSection: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 8, padding: theme.spacing.sm, marginBottom: theme.spacing.sm, }, seriesHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 4, }, seriesLabel: { fontSize: 12, fontWeight: '500', color: theme.colors.textSecondary, marginLeft: 4, }, seriesText: { fontSize: 14, color: theme.colors.textPrimary, fontWeight: '500', }, // Footer Section footer: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, footerLeft: { flex: 1, }, dateText: { fontSize: 12, color: theme.colors.textMuted, fontFamily: theme.typography.fontFamily.regular, }, processedText: { fontSize: 11, color: theme.colors.textSecondary, marginTop: 2, fontFamily: theme.typography.fontFamily.regular, }, footerRight: { flexDirection: 'row', alignItems: 'center', }, caseId: { fontSize: 12, color: theme.colors.textSecondary, marginRight: theme.spacing.xs, fontWeight: '500', }, }); export default PatientCard; /* * End of File: PatientCard.tsx * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */