362 lines
12 KiB
TypeScript
362 lines
12 KiB
TypeScript
/*
|
|
* File: PatientCard.tsx
|
|
* Description: Patient card component displaying patient information and status
|
|
* 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 { Patient } from '../../../shared/types/patient';
|
|
import { getPriorityColor, getStatusColor, calculateAge } from '../../../shared/utils/helpers';
|
|
|
|
/**
|
|
* PatientCardProps Interface
|
|
*
|
|
* Purpose: Defines the props required by the PatientCard component
|
|
*
|
|
* Props:
|
|
* - patient: Patient data object containing all patient information
|
|
* - onPress: Callback function triggered when the card is pressed
|
|
*/
|
|
interface PatientCardProps {
|
|
patient: Patient;
|
|
onPress: (patient: Patient) => void;
|
|
}
|
|
|
|
/**
|
|
* PatientCard Component
|
|
*
|
|
* Purpose: Display comprehensive patient information in a compact, touchable card format
|
|
*
|
|
* Features:
|
|
* 1. Patient identification (name, age, gender, MRN)
|
|
* 2. Priority and status badges with color coding
|
|
* 3. Location information (bed, room, attending physician)
|
|
* 4. Real-time vital signs display
|
|
* 5. Allergy warnings (if any)
|
|
* 6. Current diagnosis and last update time
|
|
*
|
|
* Data Display:
|
|
* - Header: Patient name, demographics, and status badges
|
|
* - Location: Bed/room assignment and attending physician
|
|
* - Vital Signs: BP, HR, Temperature, Oxygen saturation
|
|
* - Allergies: Warning display for known allergies
|
|
* - Footer: Current diagnosis and timestamp
|
|
*
|
|
* Interaction:
|
|
* - Touchable card that navigates to detailed patient view
|
|
* - Visual feedback with activeOpacity for better UX
|
|
*/
|
|
export const PatientCard: React.FC<PatientCardProps> = ({
|
|
patient,
|
|
onPress,
|
|
}) => {
|
|
// ============================================================================
|
|
// DATA PROCESSING
|
|
// ============================================================================
|
|
|
|
// Calculate patient age from date of birth
|
|
const age = calculateAge(patient.dateOfBirth);
|
|
|
|
// Get color coding for priority and status badges
|
|
const priorityColor = getPriorityColor(patient.priority); // Color based on priority level
|
|
const statusColor = getStatusColor(patient.status); // Color based on patient status
|
|
|
|
/**
|
|
* formatVitalSigns Function
|
|
*
|
|
* Purpose: Format vital signs data for display in the card
|
|
*
|
|
* Returns:
|
|
* - bp: Blood pressure in systolic/diastolic format
|
|
* - hr: Heart rate value
|
|
* - temp: Temperature in Celsius
|
|
* - o2: Oxygen saturation percentage
|
|
*/
|
|
const formatVitalSigns = () => {
|
|
const { vitalSigns } = patient;
|
|
return {
|
|
bp: `${vitalSigns.bloodPressure.systolic}/${vitalSigns.bloodPressure.diastolic}`, // Format: 120/80
|
|
hr: vitalSigns.heartRate.value, // Heart rate: 75
|
|
temp: vitalSigns.temperature.value, // Temperature: 37.2
|
|
o2: vitalSigns.oxygenSaturation.value, // Oxygen: 98
|
|
};
|
|
};
|
|
|
|
// Format vital signs for display
|
|
const vitals = formatVitalSigns();
|
|
|
|
// ============================================================================
|
|
// RENDER SECTION
|
|
// ============================================================================
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
style={styles.container}
|
|
onPress={() => onPress(patient)} // Navigate to patient details
|
|
activeOpacity={0.7} // Visual feedback on touch
|
|
>
|
|
{/* ========================================================================
|
|
* HEADER SECTION - Patient identification and status badges
|
|
* ======================================================================== */}
|
|
<View style={styles.header}>
|
|
{/* Patient information section */}
|
|
<View style={styles.patientInfo}>
|
|
{/* Patient full name */}
|
|
<Text style={styles.patientName}>
|
|
{patient.firstName} {patient.lastName}
|
|
</Text>
|
|
{/* Patient demographics and MRN */}
|
|
<Text style={styles.patientDetails}>
|
|
{age} years • {patient.gender} • MRN: {patient.mrn}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Status badges section */}
|
|
<View style={styles.badges}>
|
|
{/* Priority badge with color coding */}
|
|
<View style={[styles.badge, { backgroundColor: priorityColor + '20' }]}>
|
|
<Text style={[styles.badgeText, { color: priorityColor }]}>
|
|
{patient.priority}
|
|
</Text>
|
|
</View>
|
|
{/* Status badge with color coding */}
|
|
<View style={[styles.badge, { backgroundColor: statusColor + '20' }]}>
|
|
<Text style={[styles.badgeText, { color: statusColor }]}>
|
|
{patient.status}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* ========================================================================
|
|
* LOCATION SECTION - Bed assignment and attending physician
|
|
* ======================================================================== */}
|
|
<View style={styles.locationInfo}>
|
|
{/* Bed and room location */}
|
|
<Text style={styles.locationText}>
|
|
Bed {patient.bedNumber} • Room {patient.roomNumber}
|
|
</Text>
|
|
{/* Attending physician */}
|
|
<Text style={styles.attendingText}>
|
|
Dr. {patient.attendingPhysician}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* ========================================================================
|
|
* VITAL SIGNS SECTION - Real-time patient vitals
|
|
* ======================================================================== */}
|
|
<View style={styles.vitalSigns}>
|
|
{/* Blood Pressure */}
|
|
<View style={styles.vitalItem}>
|
|
<Text style={styles.vitalLabel}>BP</Text>
|
|
<Text style={styles.vitalValue}>{vitals.bp}</Text>
|
|
</View>
|
|
{/* Heart Rate */}
|
|
<View style={styles.vitalItem}>
|
|
<Text style={styles.vitalLabel}>HR</Text>
|
|
<Text style={styles.vitalValue}>{vitals.hr}</Text>
|
|
</View>
|
|
{/* Temperature */}
|
|
<View style={styles.vitalItem}>
|
|
<Text style={styles.vitalLabel}>Temp</Text>
|
|
<Text style={styles.vitalValue}>{vitals.temp}°C</Text>
|
|
</View>
|
|
{/* Oxygen Saturation */}
|
|
<View style={styles.vitalItem}>
|
|
<Text style={styles.vitalLabel}>O₂</Text>
|
|
<Text style={styles.vitalValue}>{vitals.o2}%</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* ========================================================================
|
|
* ALLERGIES SECTION - Warning display for known allergies
|
|
* ======================================================================== */}
|
|
{patient.allergies.length > 0 && (
|
|
<View style={styles.allergiesContainer}>
|
|
<Text style={styles.allergiesLabel}>Allergies:</Text>
|
|
<Text style={styles.allergiesText}>
|
|
{patient.allergies.map(a => a.name).join(', ')}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
|
|
{/* ========================================================================
|
|
* FOOTER SECTION - Diagnosis and last update time
|
|
* ======================================================================== */}
|
|
<View style={styles.footer}>
|
|
{/* Current diagnosis */}
|
|
<Text style={styles.diagnosisText}>
|
|
{patient.currentDiagnosis}
|
|
</Text>
|
|
{/* Last update timestamp */}
|
|
<Text style={styles.timeText}>
|
|
Updated {new Date(patient.lastUpdated).toLocaleTimeString()}
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// STYLES SECTION
|
|
// ============================================================================
|
|
|
|
const styles = StyleSheet.create({
|
|
// Main card container with shadow and rounded corners
|
|
container: {
|
|
backgroundColor: theme.colors.cardBackground,
|
|
borderRadius: theme.borderRadius.large,
|
|
padding: theme.spacing.md,
|
|
marginHorizontal: theme.spacing.md,
|
|
marginVertical: theme.spacing.sm,
|
|
...theme.shadows.small, // Add subtle shadow for elevation
|
|
},
|
|
|
|
// Header section with patient info and badges
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'flex-start',
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
|
|
// Patient information container
|
|
patientInfo: {
|
|
flex: 1, // Take available space
|
|
},
|
|
|
|
// Patient name styling
|
|
patientName: {
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
// Patient details styling (age, gender, MRN)
|
|
patientDetails: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
|
|
// Badges container for priority and status
|
|
badges: {
|
|
flexDirection: 'row',
|
|
gap: theme.spacing.xs, // Space between badges
|
|
},
|
|
|
|
// Individual badge styling
|
|
badge: {
|
|
paddingHorizontal: theme.spacing.sm,
|
|
paddingVertical: theme.spacing.xs,
|
|
borderRadius: theme.borderRadius.small,
|
|
},
|
|
|
|
// Badge text styling
|
|
badgeText: {
|
|
fontSize: theme.typography.fontSize.caption,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
},
|
|
|
|
// Location information container
|
|
locationInfo: {
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
|
|
// Bed and room location text
|
|
locationText: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.textPrimary,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
},
|
|
|
|
// Attending physician text
|
|
attendingText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
|
|
// Vital signs container with background
|
|
vitalSigns: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
backgroundColor: theme.colors.backgroundAccent, // Light background for vitals
|
|
borderRadius: theme.borderRadius.medium,
|
|
padding: theme.spacing.sm,
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
|
|
// Individual vital sign item
|
|
vitalItem: {
|
|
alignItems: 'center', // Center align label and value
|
|
},
|
|
|
|
// Vital sign label (BP, HR, Temp, O₂)
|
|
vitalLabel: {
|
|
fontSize: theme.typography.fontSize.caption,
|
|
color: theme.colors.textSecondary,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
// Vital sign value styling
|
|
vitalValue: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.textPrimary,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
},
|
|
|
|
// Allergies container
|
|
allergiesContainer: {
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
|
|
// Allergies label with warning color
|
|
allergiesLabel: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
color: theme.colors.warning, // Warning color for allergies
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
// Allergies text listing
|
|
allergiesText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
|
|
// Footer container with diagnosis and timestamp
|
|
footer: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
|
|
// Current diagnosis text
|
|
diagnosisText: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.textPrimary,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
flex: 1, // Take available space
|
|
},
|
|
|
|
// Last update timestamp
|
|
timeText: {
|
|
fontSize: theme.typography.fontSize.caption,
|
|
color: theme.colors.textMuted,
|
|
},
|
|
});
|
|
|
|
/*
|
|
* End of File: PatientCard.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/ |