NeoScan_Physician/app/modules/Dashboard/components/PredictionCard.tsx
2025-08-22 00:24:24 +05:30

456 lines
13 KiB
TypeScript

/*
* File: PredictionCard.tsx
* Description: Prediction card component for displaying AI prediction data and patient information
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
Image,
} from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import { theme } from '../../../theme/theme';
import type { PredictionData } from '../types/predictions';
// ============================================================================
// INTERFACES
// ============================================================================
interface PredictionCardProps {
prediction: PredictionData;
onPress: () => void;
}
// ============================================================================
// PREDICTION CARD COMPONENT
// ============================================================================
/**
* PredictionCard Component
*
* Purpose: Display AI prediction data with patient information in a card format
*
* Features:
* - Patient basic information
* - AI prediction results
* - Confidence scores
* - Clinical urgency
* - Feedback information
* - Modern card design
*/
export const PredictionCard: React.FC<PredictionCardProps> = ({
prediction,
onPress,
}) => {
// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================
/**
* Get Urgency Color Configuration
*
* Purpose: Get color and icon based on clinical urgency
*/
const getUrgencyConfig = (urgency: string) => {
switch (urgency.toLowerCase()) {
case 'critical':
return {
color: theme.colors.error,
icon: 'alert-triangle',
bgColor: '#FFEBEE'
};
case 'urgent':
return {
color: theme.colors.warning,
icon: 'clock',
bgColor: '#FFF3E0'
};
case 'non-urgent':
return {
color: theme.colors.success,
icon: 'check-circle',
bgColor: '#E8F5E8'
};
default:
return {
color: theme.colors.primary,
icon: 'info',
bgColor: '#E3F2FD'
};
}
};
/**
* Get Confidence Color
*
* Purpose: Get color based on confidence score
*/
const getConfidenceColor = (confidence: number) => {
if (confidence >= 0.8) return theme.colors.success;
if (confidence >= 0.6) return theme.colors.warning;
return theme.colors.error;
};
/**
* Format Date
*
* Purpose: Format processed date for display
*/
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
};
// ============================================================================
// RENDER
// ============================================================================
const urgencyConfig = getUrgencyConfig(prediction.prediction.clinical_urgency);
const confidenceColor = getConfidenceColor(prediction.prediction.confidence_score);
return (
<TouchableOpacity
style={styles.container}
onPress={onPress}
activeOpacity={0.7}
>
{/* Header Section */}
<View style={styles.header}>
<View style={styles.patientInfo}>
<Text style={styles.patientName}>
{prediction.patientdetails.Name || 'Unknown Patient'}
</Text>
<Text style={styles.patientDetails}>
{prediction.patientdetails.PatID} {prediction.patientdetails.PatAge} {prediction.patientdetails.PatSex}
</Text>
</View>
{/* Urgency Badge */}
<View style={[styles.urgencyBadge, { backgroundColor: urgencyConfig.bgColor }]}>
<Icon name={urgencyConfig.icon} size={16} color={urgencyConfig.color} />
<Text style={[styles.urgencyText, { color: urgencyConfig.color }]}>
{prediction.prediction.clinical_urgency}
</Text>
</View>
</View>
{/* Prediction Results Section */}
<View style={styles.predictionSection}>
<View style={styles.predictionHeader}>
<Icon name="activity" size={20} color={theme.colors.primary} />
<Text style={styles.predictionTitle}>AI Prediction</Text>
</View>
<View style={styles.predictionDetails}>
<View style={styles.predictionRow}>
<Text style={styles.predictionLabel}>Finding:</Text>
<Text style={styles.predictionValue}>
{prediction.prediction.label}
</Text>
</View>
<View style={styles.predictionRow}>
<Text style={styles.predictionLabel}>Type:</Text>
<Text style={styles.predictionValue}>
{prediction.prediction.finding_type}
</Text>
</View>
<View style={styles.predictionRow}>
<Text style={styles.predictionLabel}>Confidence:</Text>
<View style={styles.confidenceContainer}>
<Text style={[styles.confidenceValue, { color: confidenceColor }]}>
{(prediction.prediction.confidence_score * 100).toFixed(1)}%
</Text>
<View style={[styles.confidenceBar, { backgroundColor: confidenceColor }]} />
</View>
</View>
</View>
</View>
{/* Medical Information Section */}
<View style={styles.medicalSection}>
<View style={styles.medicalRow}>
<View style={styles.medicalItem}>
<Icon name="home" size={14} color={theme.colors.textSecondary} />
<Text style={styles.medicalText} numberOfLines={1} ellipsizeMode="tail">
{prediction.patientdetails.InstName || 'Unknown Institution'}
</Text>
</View>
<View style={styles.medicalItem}>
<Icon name="activity" size={14} color={theme.colors.textSecondary} />
<Text style={styles.medicalText} numberOfLines={1} ellipsizeMode="tail">
{prediction.patientdetails.Modality || 'Unknown Modality'}
</Text>
</View>
</View>
<View style={styles.medicalRow}>
<View style={styles.medicalItem}>
<Icon name="calendar" size={14} color={theme.colors.textSecondary} />
<Text style={styles.medicalText} numberOfLines={1} ellipsizeMode="tail">
{formatDate(prediction.processed_at)}
</Text>
</View>
<View style={styles.medicalItem}>
<Icon name="layers" size={14} color={theme.colors.textSecondary} />
<Text style={styles.medicalText} numberOfLines={1} ellipsizeMode="tail">
{prediction.prediction.processing_info.frame_count} Frames
</Text>
</View>
</View>
</View>
{/* Feedback Section */}
{prediction.has_provided_feedback && (
<View style={styles.feedbackSection}>
<View style={styles.feedbackHeader}>
<Icon name="message-circle" size={16} color={theme.colors.success} />
<Text style={styles.feedbackTitle}>Feedback Available</Text>
</View>
<View style={styles.feedbackDetails}>
<Text style={styles.feedbackCount}>
{prediction.user_feedback_count} feedback(s)
</Text>
<Text style={styles.feedbackType}>
Latest: {prediction.latest_feedback_type}
</Text>
</View>
</View>
)}
{/* Footer */}
<View style={styles.footer}>
<Text style={styles.processedText}>
Processed: {formatDate(prediction.processed_at)}
</Text>
<View style={styles.actionButton}>
<Icon name="chevron-right" size={16} color={theme.colors.primary} />
</View>
</View>
</TouchableOpacity>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
container: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.md,
marginBottom: theme.spacing.md,
...theme.shadows.primary,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: theme.spacing.md,
},
patientInfo: {
flex: 1,
marginRight: theme.spacing.sm,
},
patientName: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
patientDetails: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
},
urgencyBadge: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
borderRadius: theme.borderRadius.small,
gap: theme.spacing.xs,
},
urgencyText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
textTransform: 'capitalize',
},
predictionSection: {
marginBottom: theme.spacing.md,
},
predictionHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.sm,
gap: theme.spacing.sm,
},
predictionTitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
predictionDetails: {
gap: theme.spacing.xs,
},
predictionRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
predictionLabel: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
},
predictionValue: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
textTransform: 'capitalize',
},
confidenceContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: theme.spacing.xs,
},
confidenceValue: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.bold,
},
confidenceBar: {
width: 20,
height: 4,
borderRadius: 2,
},
medicalSection: {
marginBottom: theme.spacing.md,
paddingTop: theme.spacing.md,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
},
medicalRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: theme.spacing.sm,
},
medicalItem: {
flexDirection: 'row',
alignItems: 'center',
gap: theme.spacing.md,
flex: 1,
maxWidth: '48%', // Prevent items from taking too much space
},
medicalText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
flex: 1, // Allow text to take remaining space
height: 20, // Uniform height for all medical text
lineHeight: 20, // Ensure text is vertically centered
},
feedbackSection: {
marginBottom: theme.spacing.md,
paddingTop: theme.spacing.md,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
},
feedbackHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.xs,
gap: theme.spacing.xs,
},
feedbackTitle: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.success,
},
feedbackDetails: {
flexDirection: 'row',
justifyContent: 'space-between',
},
feedbackCount: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
},
feedbackType: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
textTransform: 'capitalize',
},
footer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: theme.spacing.md,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
},
processedText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
},
actionButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: theme.colors.backgroundAlt,
justifyContent: 'center',
alignItems: 'center',
},
});
/*
* End of File: PredictionCard.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/