/* * 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 { MedicalCase, PatientDetails, Series } from '../../../shared/types'; // ============================================================================ // INTERFACES // ============================================================================ interface PatientCardProps { patient: MedicalCase; 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 * - Case type with color coding * - Series information * - Time since created * - Emergency alert for critical cases * - Modern ER-focused design */ const PatientCard: React.FC = ({ patient, onPress, onEmergencyPress, }) => { // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ /** * Parse JSON strings safely * * Purpose: Handle JSON string or object parsing for patient data * * @param jsonString - JSON string or object * @returns Parsed object or empty object */ const parseJsonSafely = (jsonString: string | object) => { if (typeof jsonString === 'object') { return jsonString; } if (typeof jsonString === 'string') { try { return JSON.parse(jsonString); } catch (error) { console.warn('Failed to parse JSON:', error); return {}; } } return {}; }; /** * Get Case Type Color Configuration * * Purpose: Get color and icon based on case type * * @param type - Case type * @returns Color configuration object */ const getCaseTypeConfig = (type: string) => { switch (type) { case 'Critical': return { color: theme.colors.error, icon: 'alert-triangle', bgColor: '#FFF5F5' }; case 'Emergency': return { color: '#FF8C00', icon: 'alert-circle', bgColor: '#FFF8E1' }; case 'Routine': return { color: theme.colors.success, icon: 'check-circle', bgColor: '#F0FFF4' }; 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) { case 'CT': return '#4A90E2'; case 'MR': return '#7B68EE'; case 'DX': return '#50C878'; 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' }); }; // ============================================================================ // DATA EXTRACTION // ============================================================================ const patientDetails = parseJsonSafely(patient.patientdetails); const patientData = patientDetails.patientdetails || patientDetails; const series = parseJsonSafely(patient.series); const typeConfig = getCaseTypeConfig(patient.type); // ============================================================================ // RENDER HELPERS // ============================================================================ /** * Render Case Type Badge * * Purpose: Render case type indicator badge */ const renderTypeBadge = () => ( {patient.type} ); /** * Render Emergency Button * * Purpose: Render emergency alert button for critical cases */ const renderEmergencyButton = () => { if (patient.type !== 'Critical') { return null; } return ( ALERT ); }; // ============================================================================ // MAIN RENDER // ============================================================================ return ( {/* Header Section */} {patientData.Name || 'Unknown Patient'} ID: {patientData.PatID || 'N/A'} • {patientData.PatAge || 'N/A'}y • {patientData.PatSex || 'N/A'} {renderTypeBadge()} {renderEmergencyButton()} {/* Medical Information Section */} Modality {patientData.Modality || 'N/A'} Status {patientData.Status || 'Unknown'} Report {patientData.ReportStatus || 'Pending'} {/* Institution */} {patientData.InstName || 'Unknown Institution'} {/* Series Information */} Series Information {Array.isArray(series) ? series.length : 0} Series Available {/* Footer */} {formatDate(patient.created_at)} Case #{patient.id} ); }; // ============================================================================ // 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, }, // Type Badge typeBadge: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, marginRight: theme.spacing.xs, }, typeText: { fontSize: 10, fontWeight: 'bold', color: theme.colors.background, 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', }, dateText: { fontSize: 12, color: theme.colors.textMuted, 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. */