/* * File: SeriesDetailScreen.tsx * Description: Detailed series information screen with predictions and feedback * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. * * Features: * - Complete series information and metadata * - AI predictions and findings for the series * - Feedback history from physicians * - Floating feedback button for new feedback * - Responsive design for different screen sizes * - Integration with patient data and feedback system * - Tabbed interface for better organization */ import React, { useEffect, useState, useCallback } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Alert, Dimensions, Image, FlatList, RefreshControl, TextInput, } from 'react-native'; import { theme } from '../../../theme/theme'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; import Icon from 'react-native-vector-icons/Feather'; import { SafeAreaView } from 'react-native-safe-area-context'; import { DicomViewerModal } from '../../../shared/components'; // Import types and API import { patientAPI } from '../services/patientAPI'; import { selectUser } from '../../Auth/redux/authSelectors'; import { API_CONFIG } from '../../../shared/utils'; import { SeriesDetailScreenProps } from '../navigation/navigationTypes'; // Get screen dimensions const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); // ============================================================================ // INTERFACES // ============================================================================ interface Feedback { feedback_id: string; user_id: string; feedback_text: string; is_positive: boolean; email: string; created_at: string; prediction_id: number; prediction_file_path: string; series_number: string; feedback_type: string; } interface Prediction { id: number; file_path: string; prediction: { label: string; finding_type: string; clinical_urgency: string; confidence_score: number; detailed_results: any; finding_category: string; primary_severity: string; anatomical_location: string; // Hemorrhage type properties epidural?: boolean; subdural?: boolean; intraparenchymal?: boolean; subarachnoid?: boolean; intraventricular?: boolean; epidural_percentage?: number; subdural_percentage?: number; intraparenchymal_percentage?: number; subarachnoid_percentage?: number; intraventricular_percentage?: number; midline_shift?: number; }; processed_at: string; preview: string; } // Tab types type TabType = 'series' | 'ai' | 'feedback'; // ============================================================================ // SERIES DETAIL SCREEN COMPONENT // ============================================================================ /** * SeriesDetailScreen Component * * Purpose: Detailed view of a specific DICOM series with predictions and feedback * * Features: * - Complete series information and metadata * - AI predictions and findings display * - Feedback history from physicians * - Floating feedback button for new feedback * - Responsive design for different screen sizes * - Integration with patient data and feedback system * - Tabbed interface for better organization */ const SeriesDetailScreen: React.FC = ({ navigation, route }) => { // ============================================================================ // STATE MANAGEMENT // ============================================================================ const dispatch = useAppDispatch(); // Route parameters const { patientId, patientName, seriesNumber, seriesData, patientData, onFeedbackSubmitted } = route.params; // Redux state const user = useAppSelector(selectUser); // Local state const [isLoading, setIsLoading] = useState(false); const [isRefreshing, setIsRefreshing] = useState(false); const [error, setError] = useState(null); // Tab state const [activeTab, setActiveTab] = useState('series'); // Local patient data state for real-time updates const [localPatientData, setLocalPatientData] = useState(patientData); // Feedback state const [showFeedbackModal, setShowFeedbackModal] = useState(false); const [selectedPrediction, setSelectedPrediction] = useState(null); const [feedbackText, setFeedbackText] = useState(''); const [isPositive, setIsPositive] = useState(null); const [isSubmittingFeedback, setIsSubmittingFeedback] = useState(false); // Feedback result modal state const [showFeedbackResultModal, setShowFeedbackResultModal] = useState(false); const [feedbackResult, setFeedbackResult] = useState<{ type: 'success' | 'error'; title: string; message: string; } | null>(null); // Track newly added feedback for visual indication const [newFeedbackIds, setNewFeedbackIds] = useState>(new Set()); // DICOM Modal state const [dicomModalVisible, setDicomModalVisible] = useState(false); const [selectedDicomData, setSelectedDicomData] = useState<{ dicomUrl: string; prediction: Prediction; imageIndex: number; } | null>(null); // ============================================================================ // EFFECTS // ============================================================================ /** * Component Mount Effect * * Purpose: Set navigation title and initialize screen */ useEffect(() => { navigation.setOptions({ title: `Series ${seriesNumber}`, headerShown: false, }); }, [navigation, seriesNumber]); /** * Sync Local Patient Data Effect * * Purpose: Keep local patient data in sync with route params */ useEffect(() => { setLocalPatientData(patientData); }, [patientData]); // ============================================================================ // EVENT HANDLERS // ============================================================================ /** * Refresh Patient Data * * Purpose: Fetch updated patient data including new feedback */ const refreshPatientData = useCallback(async () => { if (!user?.access_token) return; try { const response: any = await patientAPI.getPatientDetailsById(patientId, user.access_token); if (response.ok && response.data && response.data.data) { // Update the local patient data with fresh data from API // This will include the newly submitted feedback const updatedPatientData = response.data.data; setLocalPatientData(updatedPatientData); // Also update the route params for consistency route.params.patientData = updatedPatientData; setError(null); } } catch (err: any) { console.log('Error refreshing patient data:', err.message); // Don't show error to user for background refresh } }, [patientId, user?.access_token, route.params]); /** * Handle Back Navigation * * Purpose: Navigate back to previous screen */ const handleBackPress = useCallback(() => { navigation.goBack(); }, [navigation]); /** * Handle DICOM Image Press * * Purpose: Open DICOM viewer modal for selected prediction image * * @param prediction - Prediction data containing DICOM file path * @param imageIndex - Index of the image in the series */ const handleDicomImagePress = useCallback((prediction: Prediction, imageIndex: number) => { if (prediction?.file_path) { const dicomUrl = API_CONFIG.BASE_URL + '/api/dicom' + prediction.file_path; console.log('DICOM URL:', dicomUrl); setSelectedDicomData({ dicomUrl, prediction, imageIndex, }); setDicomModalVisible(true); } else { Alert.alert( 'No DICOM Available', 'No DICOM file path is available for this image.', [{ text: 'OK' }] ); } }, []); /** * Handle Close DICOM Modal * * Purpose: Close DICOM viewer modal and reset state */ const handleCloseDicomModal = useCallback(() => { setDicomModalVisible(false); setSelectedDicomData(null); }, []); /** * Handle Refresh * * Purpose: Pull-to-refresh functionality */ const handleRefresh = useCallback(async () => { setIsRefreshing(true); // Refresh patient data to get latest information await refreshPatientData(); setIsRefreshing(false); }, [refreshPatientData]); /** * Handle Open Feedback Modal * * Purpose: Open feedback modal for a specific prediction * * @param prediction - Prediction data for feedback */ const handleOpenFeedback = useCallback((prediction: Prediction) => { setSelectedPrediction(prediction); setFeedbackText(''); setIsPositive(null); setShowFeedbackModal(true); }, []); /** * Handle Submit Feedback * * Purpose: Submit feedback to API */ const handleSubmitFeedback = useCallback(async () => { if (!selectedPrediction || !feedbackText.trim() || isPositive === null) { setFeedbackResult({ type: 'error', title: 'Validation Error', message: 'Please provide all required feedback information' }); setShowFeedbackResultModal(true); return; } try { setIsSubmittingFeedback(true); if (!patientId) { throw new Error('Patient ID not available'); } const feedbackPayload = { patid: patientId, prediction_id: selectedPrediction.id, feedback_text: feedbackText.trim(), is_positive: isPositive }; console.log('Submitting feedback payload:', feedbackPayload); // Call the actual API const response = await patientAPI.submitFeedback(feedbackPayload, user?.access_token); console.log('Feedback response:', response); if (!response.ok) { throw new Error(response.problem || 'Failed to submit feedback'); } // Show success message setFeedbackResult({ type: 'success', title: 'Feedback Submitted', message: 'Your feedback has been recorded successfully.' }); setShowFeedbackResultModal(true); // Track this feedback as newly added for visual indication const newFeedbackId = `new_${Date.now()}`; setNewFeedbackIds(prev => new Set(prev).add(newFeedbackId)); // Refresh patient data to get updated feedback await refreshPatientData(); // Notify parent screen to refresh its data as well // This ensures PatientDetailsScreen shows updated information when user navigates back if (onFeedbackSubmitted) { onFeedbackSubmitted(); } } catch (error: any) { setFeedbackResult({ type: 'error', title: 'Error', message: error.message || 'Failed to submit feedback. Please try again.' }); setShowFeedbackResultModal(true); } finally { setIsSubmittingFeedback(false); } }, [selectedPrediction, feedbackText, isPositive, patientId, user?.access_token, refreshPatientData, onFeedbackSubmitted]); /** * Handle Close Feedback Modal * * Purpose: Close feedback modal and reset state */ const handleCloseFeedback = useCallback(() => { setShowFeedbackModal(false); setSelectedPrediction(null); setFeedbackText(''); setIsPositive(null); }, []); /** * Handle Feedback Result Modal Close * * Purpose: Close feedback result modal and reset form if success */ const handleFeedbackResultClose = useCallback(() => { setShowFeedbackResultModal(false); setFeedbackResult(null); // If it was a success, also close the feedback modal and reset form if (feedbackResult?.type === 'success') { setShowFeedbackModal(false); setSelectedPrediction(null); setFeedbackText(''); setIsPositive(null); } }, [feedbackResult?.type]); /** * Auto-close Success Modal Effect * * Purpose: Automatically close success modal after 2 seconds */ useEffect(() => { if (feedbackResult?.type === 'success') { const timer = setTimeout(() => { handleFeedbackResultClose(); }, 2000); return () => clearTimeout(timer); } }, [feedbackResult?.type, handleFeedbackResultClose]); /** * Clear New Feedback Badges Effect * * Purpose: Clear "New" badges after 30 seconds to avoid UI clutter */ useEffect(() => { if (newFeedbackIds.size > 0) { const timer = setTimeout(() => { setNewFeedbackIds(new Set()); }, 30000); // 30 seconds return () => clearTimeout(timer); } }, [newFeedbackIds.size]); // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ /** * Get Clinical Urgency Color * * Purpose: Get appropriate color for clinical urgency * * @param urgency - Clinical urgency level */ const getUrgencyColor = (urgency: string) => { switch (urgency.toLowerCase()) { case 'urgent': return theme.colors.error; case 'semi-urgent': return theme.colors.warning; case 'non-urgent': return theme.colors.success; default: return theme.colors.info; } }; /** * Get Feedback Type Color * * Purpose: Get appropriate color for feedback type * * @param feedbackType - Feedback type (positive/negative) */ const getFeedbackTypeColor = (feedbackType: string) => { switch (feedbackType.toLowerCase()) { case 'positive': return theme.colors.success; case 'negative': return theme.colors.error; default: return theme.colors.info; } }; /** * Get Percentage Color * * Purpose: Get appropriate color based on percentage value * * @param percentage - Percentage value (0-100) */ const getPercentageColor = (percentage: number) => { if (percentage >= 70) { return theme.colors.error; // High detection - Red } else if (percentage >= 40) { return theme.colors.warning; // Medium detection - Orange } else if (percentage >= 10) { return theme.colors.info; // Low detection - Blue } else { return theme.colors.success; // No detection - Green } }; /** * Get Percentage Status * * Purpose: Get status text based on percentage value * * @param percentage - Percentage value (0-100) */ const getPercentageStatus = (percentage: number) => { if (percentage >= 70) { return 'HIGH'; } else if (percentage >= 40) { return 'MEDIUM'; } else if (percentage >= 10) { return 'LOW'; } else { return 'NONE'; } }; /** * Get Percentage Value * * Purpose: Extract percentage value from prediction data with flexible field naming * * @param prediction - Prediction object * @param type - Hemorrhage type (epidural, subdural, etc.) */ const getPercentageValue = (prediction: any, type: string): number => { // First, try to get from detailed_results.hemorrhage_detection if (prediction.detailed_results?.hemorrhage_detection) { const hemorrhageData = prediction.detailed_results.hemorrhage_detection; // Map our types to the actual field names in the data const fieldMapping: { [key: string]: string } = { 'epidural': 'Epidural', 'subdural': 'Subdural', 'subarachnoid': 'Subarachnoid', 'intraparenchymal': 'Intraparenchymal', 'intraventricular': 'Intraventricular', 'midline_shift': 'Midline shift' }; const actualFieldName = fieldMapping[type]; if (actualFieldName && hemorrhageData[actualFieldName] !== undefined) { const value = hemorrhageData[actualFieldName]; // Convert decimal (0-1) to percentage (0-100) if (typeof value === 'number' && value >= 0 && value <= 1) { return value * 100; } // If it's already a percentage, return as is if (typeof value === 'number' && value >= 0 && value <= 100) { return value; } // Handle string values if (typeof value === 'string') { const numValue = parseFloat(value); if (!isNaN(numValue)) { if (numValue >= 0 && numValue <= 1) { return numValue * 100; } if (numValue >= 0 && numValue <= 100) { return numValue; } } } } } // Fallback: try different possible field names for the percentage const possibleFields = [ `${type}_percentage`, `${type}_score`, `${type}_value`, `${type}_detection`, `${type}_probability`, type ]; for (const field of possibleFields) { if (prediction[field] !== undefined && prediction[field] !== null) { const value = prediction[field]; // Handle different data types if (typeof value === 'number') { // If it's already a percentage (0-100), return as is if (value >= 0 && value <= 100) { return value; } // If it's a decimal (0-1), convert to percentage if (value >= 0 && value <= 1) { return value * 100; } // If it's a large number, assume it's already a percentage return value; } // Handle string values if (typeof value === 'string') { const numValue = parseFloat(value); if (!isNaN(numValue)) { // If it's already a percentage (0-100), return as is if (numValue >= 0 && numValue <= 100) { return numValue; } // If it's a decimal (0-1), convert to percentage if (numValue >= 0 && numValue <= 1) { return numValue * 100; } // If it's a large number, assume it's already a percentage return numValue; } } // Handle boolean values (convert to 0% or 100%) if (typeof value === 'boolean') { return value ? 100 : 0; } } } // If no valid percentage found, return 0 return 0; }; /** * Get Series Predictions * * Purpose: Get predictions for the current series */ const getSeriesPredictions = () => { if (!localPatientData?.predictions_by_series) return []; return localPatientData.predictions_by_series[seriesNumber] || []; }; /** * Get Series Feedback * * Purpose: Get feedback for the current series */ const getSeriesFeedback = () => { if (!localPatientData?.feedback_by_series) return []; return localPatientData.feedback_by_series[seriesNumber] || []; }; /** * Check if Feedback is New * * Purpose: Check if feedback was recently added for visual indication * * @param feedbackId - Feedback ID to check */ const isFeedbackNew = (feedbackId: string) => { return newFeedbackIds.has(feedbackId); }; // ============================================================================ // RENDER HELPERS // ============================================================================ /** * Render Tab Navigation * * Purpose: Render tab navigation for switching between different sections */ const renderTabNavigation = () => { const tabs = [ { id: 'series' as TabType, label: 'Series Info', icon: 'info' }, { id: 'ai' as TabType, label: 'AI Analysis', icon: 'activity' }, { id: 'feedback' as TabType, label: 'Feedback', icon: 'message-circle' } ]; return ( {tabs.map((tab) => ( setActiveTab(tab.id)} activeOpacity={0.7} > {tab.label} {/* Active Tab Indicator */} {activeTab === tab.id && ( )} ))} ); }; /** * Render Series Header * * Purpose: Render series identification and basic information */ const renderSeriesHeader = () => { if (!seriesData) return null; return ( Series {seriesData.series_num} {seriesData.series_description || 'No description available'} {seriesData.total_images} images {seriesData.modality} modality {seriesData.body_part && ( {seriesData.body_part} )} Processed {seriesData.study_date && ( {new Date(seriesData.study_date).toLocaleDateString()} )} ); }; /** * Render Series Details * * Purpose: Render detailed series information in a comprehensive format */ const renderSeriesDetails = () => { if (!seriesData) return null; return ( Series Information {/* Series Summary Bar */} Series Number {seriesData.series_num} Total Images {seriesData.total_images} Modality {seriesData.modality} {/* Detailed Series Information Card */} Series Details Complete series metadata and information Series Number {seriesData.series_num} Series Description {seriesData.series_description || 'No description available'} Total Images {seriesData.total_images} Modality {seriesData.modality} Patient ID {patientId} Patient Name {patientName} {seriesData.body_part && ( Body Part {seriesData.body_part} Study Date {seriesData.study_date ? new Date(seriesData.study_date).toLocaleDateString() : 'Not specified'} )} {seriesData.institution_name && ( Institution {seriesData.institution_name} Manufacturer {seriesData.manufacturer || 'Not specified'} )} {/* Processing Information */} Processing Information File Type DICOM Multiframe {seriesData.total_images > 1 ? 'Yes' : 'No'} Frames {seriesData.total_images} Status Processed ); }; /** * Render AI Predictions * * Purpose: Render AI predictions and findings for the series */ const renderAIPredictions = () => { const predictions = getSeriesPredictions(); // Debug: Log the prediction data structure if (predictions.length === 0) { return ( AI Analysis Results No AI Predictions No AI predictions are available for this series yet ); } // Calculate summary metrics const totalPredictions = predictions.length; const highPriorityCount = predictions.filter((p: Prediction) => p.prediction.clinical_urgency?.toLowerCase() === 'urgent' ).length; const avgConfidence = predictions.reduce((sum: number, p: Prediction) => sum + (p.prediction.confidence_score || 0), 0 ) / totalPredictions; return ( AI Analysis Results {/* AI Summary Bar */} Total Predictions {totalPredictions} predictions found High Priority {highPriorityCount > 0 ? `${highPriorityCount} urgent` : 'None'} Avg Confidence {(avgConfidence * 100).toFixed(1)}% {/* AI Predictions Card */} AI Predictions Analysis Medical scan analysis results {predictions.map((prediction: Prediction) => ( {prediction.prediction.label} {prediction.prediction.clinical_urgency} Finding Type: {prediction.prediction.finding_type} Confidence: {(prediction.prediction.confidence_score * 100).toFixed(1)}% Category: {prediction.prediction.finding_category} Severity: {prediction.prediction.primary_severity} Location: {prediction.prediction.anatomical_location} {/* Show specific hemorrhage types if they exist */} {prediction.prediction.epidural !== undefined && ( Epidural Hemorrhage: {prediction.prediction.epidural ? 'Detected' : 'Not Detected'} )} {prediction.prediction.subdural !== undefined && ( Subdural Hemorrhage: {prediction.prediction.subdural ? 'Detected' : 'Not Detected'} )} {prediction.prediction.intraparenchymal !== undefined && ( Intraparenchymal Hemorrhage: {prediction.prediction.intraparenchymal ? 'Detected' : 'Not Detected'} )} {prediction.prediction.subarachnoid !== undefined && ( Subarachnoid Hemorrhage: {prediction.prediction.subarachnoid ? 'Detected' : 'Not Detected'} )} {prediction.prediction.intraventricular !== undefined && ( Intraventricular Hemorrhage: {prediction.prediction.intraventricular ? 'Detected' : 'Not Detected'} )} {/* Additional Findings from detailed_results */} {prediction.prediction.detailed_results && ( Additional Analysis Results {/* Stroke Detection */} {prediction.prediction.detailed_results.stroke_detection && ( Stroke Detection: Normal: {((prediction.prediction.detailed_results.stroke_detection.Normal || 0) * 100).toFixed(1)}% Stroke: {((prediction.prediction.detailed_results.stroke_detection.Stroke || 0) * 100).toFixed(1)}% )} {/* Binary Hemorrhage */} {prediction.prediction.detailed_results.binary_hemorrhage && ( Hemorrhage Detection: Normal: {((prediction.prediction.detailed_results.binary_hemorrhage.Normal || 0) * 100).toFixed(1)}% Hemorrhage: {((prediction.prediction.detailed_results.binary_hemorrhage.Hemorrhage || 0) * 100).toFixed(1)}% )} )} {/* Visual Indicators Section - Show detailed findings with percentage indicators */} Detailed Findings Analysis {/* Compact Hemorrhage Type Percentage Indicators with Progress Bars */} {/* Epidural */} Epidural {getPercentageValue(prediction.prediction, 'epidural').toFixed(1)}% {/* Subdural */} Subdural {getPercentageValue(prediction.prediction, 'subdural').toFixed(1)}% {/* Intraparenchymal */} Intraparenchymal {getPercentageValue(prediction.prediction, 'intraparenchymal').toFixed(1)}% {/* Subarachnoid */} Subarachnoid {getPercentageValue(prediction.prediction, 'subarachnoid').toFixed(1)}% {/* Intraventricular */} Intraventricular {getPercentageValue(prediction.prediction, 'intraventricular').toFixed(1)}% {/* Midline Shift */} Midline Shift {getPercentageValue(prediction.prediction, 'midline_shift').toFixed(1)}% 50 ? theme.colors.error : theme.colors.warning } ]} /> {/* Summary Indicator with Overall Percentage */} Overall Assessment {/* Overall Percentage Calculation */} {(() => { const percentages = [ getPercentageValue(prediction.prediction, 'epidural'), getPercentageValue(prediction.prediction, 'subdural'), getPercentageValue(prediction.prediction, 'intraparenchymal'), getPercentageValue(prediction.prediction, 'subarachnoid'), getPercentageValue(prediction.prediction, 'intraventricular'), getPercentageValue(prediction.prediction, 'midline_shift') ]; const maxPercentage = Math.max(...percentages); const hasHemorrhage = maxPercentage > 10; // Consider >10% as detected return ( Highest Detection: {maxPercentage.toFixed(1)}% {hasHemorrhage ? 'HEMORRHAGE DETECTED' : 'NO HEMORRHAGE'} ); })()} Processed: {new Date(prediction.processed_at).toLocaleDateString()} ))} ); }; /** * Render Series Images * * Purpose: Render DICOM image previews for the series */ const renderSeriesImages = () => { const predictions = getSeriesPredictions(); if (predictions.length === 0) { return ( DICOM Images No Images Available No DICOM images are available for this series ); } // Calculate image summary metrics const totalImages = predictions.length; const imagesWithPreview = predictions.filter((p: Prediction) => p.preview).length; const imagesWithoutPreview = totalImages - imagesWithPreview; return ( DICOM Images {/* Images Summary Bar */} Total Images {totalImages} With Preview {imagesWithPreview} Format DICOM {/* Images Display Card */} Image Gallery Series image previews and metadata {predictions.map((prediction: Prediction, index: number) => ( {prediction.preview ? ( handleDicomImagePress(prediction, index)} activeOpacity={0.7} > {/* Overlay to indicate clickable */} View DICOM ) : ( No Preview )} Image {index + 1} {prediction.prediction.label} ))} ); }; /** * Render Feedback History * * Purpose: Render feedback history from physicians */ const renderFeedbackHistory = () => { const feedback = getSeriesFeedback(); if (isRefreshing) { return ( Feedback History Refreshing feedback... ); } if (feedback.length === 0) { return ( Feedback History No Feedback Yet Be the first to provide feedback on this series ); } // Calculate feedback summary metrics const totalFeedback = feedback.length; const positiveFeedback = feedback.filter((f: Feedback) => f.is_positive).length; const negativeFeedback = feedback.filter((f: Feedback) => !f.is_positive).length; return ( Feedback History {/* Feedback Summary Bar */} Total Feedback {totalFeedback} entries Positive {positiveFeedback} Negative {negativeFeedback} {/* Feedback History Card */} Clinical Feedback Radiologist insights and corrections {feedback.map((feedbackItem: Feedback) => ( {feedbackItem.feedback_type} {isFeedbackNew(feedbackItem.feedback_id) && ( NEW )} {new Date(feedbackItem.created_at).toLocaleDateString()} {feedbackItem.feedback_text} {feedbackItem.email} Prediction ID: {feedbackItem.prediction_id} ))} ); }; // ============================================================================ // MAIN RENDER // ============================================================================ return ( {/* Header */} Series {seriesNumber} {patientName} {/* Tab Navigation */} {renderTabNavigation()} {/* Content */} } > {/* Series Header */} {renderSeriesHeader()} {/* Tab Content */} {activeTab === 'series' && ( <> {/* Series Details */} {renderSeriesDetails()} {/* Series Images */} {renderSeriesImages()} )} {activeTab === 'ai' && ( <> {/* AI Predictions */} {renderAIPredictions()} )} {activeTab === 'feedback' && ( <> {/* Feedback History */} {renderFeedbackHistory()} )} {/* Floating Feedback Button - Show on all tabs */} { const predictions = getSeriesPredictions(); if (predictions.length > 0) { handleOpenFeedback(predictions[0]); } else { Alert.alert('No Predictions', 'No AI predictions available for feedback'); } }} activeOpacity={0.8} > {/* Feedback Modal */} {showFeedbackModal && selectedPrediction && ( Provide Feedback {/* Prediction Info */} AI Prediction: {selectedPrediction.prediction.label} Confidence: {(selectedPrediction.prediction.confidence_score * 100).toFixed(1)}% • Type: {selectedPrediction.prediction.finding_type} {/* Prediction Accuracy Selection */} Is this prediction accurate? {[ { key: 'true', label: 'Yes (Positive)', color: theme.colors.success, icon: 'check-circle', value: true }, { key: 'false', label: 'No (Negative)', color: theme.colors.error, icon: 'x-circle', value: false } ].map((option) => ( setIsPositive(option.value)} style={[ styles.predictionAccuracyButton, isPositive === option.value && styles.predictionAccuracyButtonActive, { borderColor: option.color } ]} > {option.label} ))} {/* Feedback Text Input */} Your Feedback Cancel {isSubmittingFeedback ? ( Submitting... ) : ( Submit Feedback )} )} {/* Feedback Result Modal */} {showFeedbackResultModal && feedbackResult && ( {feedbackResult.title} {feedbackResult.message} OK )} {/* DICOM Viewer Modal */} {selectedDicomData && ( )} ); }; // ============================================================================ // STYLES // ============================================================================ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: theme.colors.background, }, // Header Styles header: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.sm, backgroundColor: theme.colors.background, borderBottomWidth: 1, borderBottomColor: theme.colors.border, }, backButton: { padding: theme.spacing.sm, marginRight: theme.spacing.sm, }, headerTitle: { flex: 1, alignItems: 'center', }, headerTitleText: { fontSize: 18, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, headerSubtitleText: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, refreshButton: { padding: theme.spacing.sm, marginLeft: theme.spacing.sm, }, // Content Styles content: { flex: 1, }, // Section Styles section: { marginBottom: theme.spacing.lg, paddingHorizontal: theme.spacing.md, }, sectionTitle: { fontSize: 18, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.md, }, // Series Header Styles seriesHeader: { paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.lg, backgroundColor: theme.colors.background, borderBottomWidth: 1, borderBottomColor: theme.colors.border, }, seriesHeaderContent: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, seriesHeaderLeft: { flex: 1, }, seriesHeaderRight: { alignItems: 'flex-end', }, seriesTitle: { fontSize: 24, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.xs, }, seriesDescription: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, marginBottom: theme.spacing.sm, }, seriesMetaRow: { flexDirection: 'row', flexWrap: 'wrap', marginTop: theme.spacing.xs, }, metaItem: { flexDirection: 'row', alignItems: 'center', marginRight: theme.spacing.md, marginBottom: theme.spacing.xs, }, metaText: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, marginLeft: theme.spacing.sm, }, studyDate: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, seriesStatusBadge: { flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.success, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.xs, borderRadius: 12, }, seriesStatusText: { fontSize: 12, color: theme.colors.background, fontFamily: theme.typography.fontFamily.bold, marginLeft: theme.spacing.sm, }, // Series Details Styles seriesDetails: { backgroundColor: theme.colors.backgroundAlt, padding: theme.spacing.md, borderRadius: 8, }, seriesDetailItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: theme.spacing.sm, }, seriesDetailLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, flex: 1, }, seriesDetailValue: { fontSize: 12, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, flex: 2, textAlign: 'right', }, // Prediction Styles predictionCard: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 8, padding: theme.spacing.sm, marginBottom: theme.spacing.sm, // shadowColor: '#000', // shadowOffset: { width: 0, height: 2 }, // shadowOpacity: 0.1, // shadowRadius: 4, // elevation: 2, }, predictionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing.sm, }, predictionLabel: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, flex: 1, }, urgencyBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, }, urgencyText: { fontSize: 10, color: theme.colors.background, textTransform: 'uppercase', fontFamily: theme.typography.fontFamily.medium, }, predictionDetails: { marginBottom: theme.spacing.sm, }, predictionDetailItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: theme.spacing.xs, }, predictionDetailLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, flex: 1, }, predictionDetailValue: { fontSize: 12, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, flex: 2, textAlign: 'right', }, predictionTimestamp: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, textAlign: 'right', }, // Image Styles imageList: { paddingRight: theme.spacing.md, }, imageContainer: { alignItems: 'center', marginRight: theme.spacing.md, }, imageClickable: { position: 'relative', marginBottom: theme.spacing.xs, borderRadius: 8, overflow: 'hidden', }, seriesImage: { width: 120, height: 120, borderRadius: 8, }, imageOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.8)', justifyContent: 'center', alignItems: 'center', opacity: 1, }, imageOverlayText: { color: theme.colors.background, fontSize: 10, fontFamily: theme.typography.fontFamily.bold, marginTop: 4, textAlign: 'center', }, noImagePlaceholder: { width: 120, height: 120, borderRadius: 8, backgroundColor: theme.colors.backgroundAlt, justifyContent: 'center', alignItems: 'center', borderWidth: 1, borderColor: theme.colors.border, borderStyle: 'dashed', marginBottom: theme.spacing.xs, }, noImageText: { fontSize: 10, color: theme.colors.textMuted, fontFamily: theme.typography.fontFamily.regular, textAlign: 'center', }, imageLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, imagePredictionLabel: { fontSize: 10, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, marginTop: theme.spacing.xs, }, // Feedback Styles feedbackCard: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 8, padding: theme.spacing.md, marginBottom: theme.spacing.sm, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, feedbackHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing.sm, }, feedbackHeaderLeft: { flexDirection: 'row', alignItems: 'center', }, feedbackTypeBadge: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, }, feedbackTypeText: { fontSize: 10, color: theme.colors.background, textTransform: 'uppercase', fontFamily: theme.typography.fontFamily.medium, }, newFeedbackBadge: { backgroundColor: theme.colors.primary, paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, marginLeft: theme.spacing.sm, }, newFeedbackBadgeText: { fontSize: 10, color: theme.colors.background, textTransform: 'uppercase', fontFamily: theme.typography.fontFamily.bold, }, feedbackDate: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, feedbackText: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, lineHeight: 20, marginBottom: theme.spacing.sm, }, feedbackFooter: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, feedbackEmail: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, feedbackPredictionId: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, // Empty State Styles emptyState: { alignItems: 'center', justifyContent: 'center', paddingVertical: theme.spacing.xl, }, emptyStateTitle: { fontSize: 18, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginTop: theme.spacing.md, marginBottom: theme.spacing.sm, }, emptyStateSubtitle: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, textAlign: 'center', lineHeight: 20, }, // Floating Feedback Button floatingFeedbackButton: { position: 'absolute', bottom: theme.spacing.lg, right: theme.spacing.lg, width: 56, height: 56, borderRadius: 28, backgroundColor: theme.colors.primary, justifyContent: 'center', alignItems: 'center', shadowColor: theme.colors.primary, shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8, elevation: 8, }, // Modal Styles modalOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.7)', justifyContent: 'center', alignItems: 'center', zIndex: 1000, }, feedbackModal: { backgroundColor: theme.colors.background, borderRadius: 12, width: '90%', maxWidth: 450, shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 10, elevation: 10, }, modalHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: theme.spacing.md, borderBottomWidth: 1, borderBottomColor: theme.colors.border, }, modalTitle: { fontSize: 20, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, closeButton: { padding: theme.spacing.sm, }, modalContent: { padding: theme.spacing.md, }, feedbackSection: { marginBottom: theme.spacing.md, }, feedbackSectionTitle: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.sm, }, feedbackPredictionInfo: { paddingTop: theme.spacing.md, marginBottom: theme.spacing.sm, }, feedbackPredictionTitle: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.xs, }, feedbackPredictionMeta: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, predictionAccuracyContainer: { flexDirection: 'row', justifyContent: 'space-around', marginBottom: theme.spacing.sm, }, predictionAccuracyButton: { flexDirection: 'row', alignItems: 'center', paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md, borderRadius: 12, borderWidth: 1, borderColor: theme.colors.border, minWidth: 120, justifyContent: 'center', }, predictionAccuracyButtonActive: { borderColor: theme.colors.primary, backgroundColor: theme.colors.primary, }, predictionAccuracyButtonText: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginLeft: theme.spacing.sm, }, predictionAccuracyButtonTextActive: { color: theme.colors.background, fontFamily: theme.typography.fontFamily.bold, }, feedbackTextInput: { borderWidth: 1, borderColor: theme.colors.border, borderRadius: 8, padding: theme.spacing.md, fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, minHeight: 100, textAlignVertical: 'top', }, modalFooter: { flexDirection: 'row', justifyContent: 'space-around', padding: theme.spacing.md, borderTopWidth: 1, borderTopColor: theme.colors.border, }, cancelButton: { paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.lg, borderRadius: 8, borderWidth: 1, borderColor: theme.colors.border, }, cancelButtonText: { fontSize: 16, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, }, submitButton: { paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.lg, borderRadius: 8, backgroundColor: theme.colors.primary, shadowColor: theme.colors.primary, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.3, shadowRadius: 4, elevation: 4, }, submitButtonDisabled: { backgroundColor: theme.colors.textMuted, opacity: 0.7, }, submitButtonText: { color: theme.colors.background, fontSize: 16, fontFamily: theme.typography.fontFamily.bold, }, submitButtonLoading: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, // Feedback Result Modal Styles feedbackResultModal: { backgroundColor: theme.colors.background, borderRadius: 16, padding: 0, width: '90%', maxWidth: 400, shadowColor: '#000000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.15, shadowRadius: 8, elevation: 8, }, feedbackResultMessage: { fontSize: 16, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, textAlign: 'center', lineHeight: 24, paddingHorizontal: theme.spacing.md, }, okButton: { paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.xl, borderRadius: 8, backgroundColor: theme.colors.primary, shadowColor: theme.colors.primary, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.3, shadowRadius: 4, elevation: 4, minWidth: 100, alignItems: 'center', }, okButtonText: { color: theme.colors.background, fontSize: 16, fontFamily: theme.typography.fontFamily.bold, }, loadingState: { alignItems: 'center', justifyContent: 'center', paddingVertical: theme.spacing.xl, }, loadingStateText: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, marginTop: theme.spacing.sm, }, // New styles for detailed series info seriesSummaryBar: { flexDirection: 'row', justifyContent: 'space-around', backgroundColor: theme.colors.backgroundAlt, paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md, borderRadius: 8, marginBottom: theme.spacing.md, }, summaryItem: { alignItems: 'center', }, summaryLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginBottom: theme.spacing.xs, }, summaryValueContainer: { backgroundColor: theme.colors.background, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.xs, borderRadius: 12, }, summaryValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, seriesDetailsCard: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 12, padding: theme.spacing.md, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing.sm, }, cardTitle: { fontSize: 18, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, cardSubtitle: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, }, detailsGrid: { marginBottom: theme.spacing.md, }, detailRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: theme.spacing.sm, }, detailColumn: { flex: 1, marginRight: theme.spacing.sm, }, detailLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginBottom: theme.spacing.xs, }, detailValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, }, processingInfo: { marginTop: theme.spacing.md, paddingTop: theme.spacing.md, borderTopWidth: 1, borderTopColor: theme.colors.border, }, processingTitle: { fontSize: 16, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.sm, }, processingGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-around', }, processingItem: { alignItems: 'center', marginBottom: theme.spacing.sm, }, processingLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, }, processingValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, statusBadge: { backgroundColor: theme.colors.success, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.xs, borderRadius: 12, }, statusText: { fontSize: 12, color: theme.colors.background, fontFamily: theme.typography.fontFamily.bold, }, // New styles for AI summary bar aiSummaryBar: { flexDirection: 'row', justifyContent: 'space-around', backgroundColor: theme.colors.backgroundAlt, paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md, borderRadius: 8, marginBottom: theme.spacing.md, }, aiSummaryItem: { alignItems: 'center', }, aiSummaryLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginBottom: theme.spacing.xs, }, aiSummaryValueContainer: { backgroundColor: theme.colors.background, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.xs, borderRadius: 12, }, aiSummaryValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, // New styles for AI predictions card aiPredictionsCard: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 12, padding: theme.spacing.md, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, // New styles for detailed results section detailedResultsSection: { marginTop: theme.spacing.md, paddingTop: theme.spacing.md, borderTopWidth: 1, borderTopColor: theme.colors.border, }, detailedResultsTitle: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.sm, }, detailedResultsGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-around', }, detailedResultItem: { alignItems: 'center', marginBottom: theme.spacing.sm, }, detailedResultLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, }, detailedResultValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, }, // New styles for additional findings additionalFindingItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: theme.spacing.sm, paddingTop: theme.spacing.sm, borderTopWidth: 1, borderTopColor: theme.colors.border, }, additionalFindingLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, }, additionalFindingValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.regular, }, // New styles for additional findings section additionalFindingsSection: { marginTop: theme.spacing.md, paddingTop: theme.spacing.md, borderTopWidth: 1, borderTopColor: theme.colors.border, }, additionalFindingsTitle: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.sm, }, additionalFindingValues: { alignItems: 'flex-end', }, // New styles for images summary bar imagesSummaryBar: { flexDirection: 'row', justifyContent: 'space-around', backgroundColor: theme.colors.backgroundAlt, paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md, borderRadius: 8, marginBottom: theme.spacing.md, }, imagesSummaryItem: { alignItems: 'center', }, imagesSummaryLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginBottom: theme.spacing.xs, }, imagesSummaryValueContainer: { backgroundColor: theme.colors.background, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.xs, borderRadius: 12, }, imagesSummaryValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, // New styles for images display card imagesDisplayCard: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 12, padding: theme.spacing.md, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, // New styles for feedback summary bar feedbackSummaryBar: { flexDirection: 'row', justifyContent: 'space-around', backgroundColor: theme.colors.backgroundAlt, paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md, borderRadius: 8, marginBottom: theme.spacing.md, }, feedbackSummaryItem: { alignItems: 'center', }, feedbackSummaryLabel: { fontSize: 12, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginBottom: theme.spacing.xs, }, feedbackSummaryValueContainer: { backgroundColor: theme.colors.background, paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.xs, borderRadius: 12, }, feedbackSummaryValue: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, }, // New styles for feedback history card feedbackHistoryCard: { backgroundColor: theme.colors.backgroundAlt, borderRadius: 12, padding: theme.spacing.sm, paddingBottom: theme.spacing.md, // shadowColor: '#000', // shadowOffset: { width: 0, height: 2 }, // shadowOpacity: 0.1, // shadowRadius: 4, // elevation: 2, }, // New styles for visual indicators section visualIndicatorsSection: { marginTop: theme.spacing.md, paddingTop: theme.spacing.md, borderTopWidth: 1, borderTopColor: theme.colors.border, }, visualIndicatorsTitle: { fontSize: 16, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.md, textAlign: 'center', }, // New styles for compact indicators compactIndicatorsContainer: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', marginBottom: theme.spacing.md, // paddingHorizontal: theme.spacing.sm, }, compactIndicatorItem: { width: '100%', paddingVertical: theme.spacing.md, paddingHorizontal: theme.spacing.md, backgroundColor: theme.colors.background, borderRadius: 12, marginBottom: theme.spacing.sm, borderWidth: 1, borderColor: theme.colors.border, // shadowColor: '#000', // shadowOffset: { width: 0, height: 1 }, // shadowOpacity: 0.05, // shadowRadius: 2, // elevation: 1, }, indicatorHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: theme.spacing.sm, }, compactIndicatorTitle: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.medium, flex: 1, marginLeft: theme.spacing.sm, }, compactIndicatorPercentage: { fontSize: 16, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginLeft: theme.spacing.sm, }, indicatorsGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-around', marginBottom: theme.spacing.md, }, indicatorCard: { backgroundColor: theme.colors.background, borderRadius: 12, padding: theme.spacing.md, marginBottom: theme.spacing.sm, minWidth: 140, alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, indicatorTitle: { fontSize: 12, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginLeft: theme.spacing.xs, textAlign: 'center', }, indicatorStatus: { paddingHorizontal: theme.spacing.sm, paddingVertical: theme.spacing.xs, borderRadius: 8, minWidth: 100, alignItems: 'center', }, indicatorStatusText: { fontSize: 10, color: theme.colors.background, fontFamily: theme.typography.fontFamily.bold, textTransform: 'uppercase', textAlign: 'center', }, summaryIndicator: { alignItems: 'center', marginTop: theme.spacing.md, }, summaryIndicatorTitle: { fontSize: 14, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.sm, }, summaryIndicatorStatus: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.sm, borderRadius: 12, minWidth: 200, justifyContent: 'center', }, summaryIndicatorText: { fontSize: 12, color: theme.colors.background, fontFamily: theme.typography.fontFamily.bold, textTransform: 'uppercase', marginLeft: theme.spacing.sm, textAlign: 'center', }, percentageContainer: { alignItems: 'center', marginBottom: theme.spacing.sm, }, percentageValue: { fontSize: 16, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.xs, }, progressBarContainer: { width: '100%', height: 6, backgroundColor: theme.colors.backgroundAlt, borderRadius: 3, marginTop: theme.spacing.xs, overflow: 'hidden', }, progressBar: { height: '100%', borderRadius: 3, minWidth: 4, }, progressBarBackground: { width: '100%', height: '100%', backgroundColor: theme.colors.background, borderRadius: 4, }, progressBarFill: { height: '100%', borderRadius: 4, minWidth: 4, // Ensure minimum width for very small percentages }, overallPercentageContainer: { alignItems: 'center', marginBottom: theme.spacing.md, }, overallPercentageLabel: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginBottom: theme.spacing.xs, }, overallPercentageValue: { fontSize: 24, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.md, }, debugSection: { marginTop: theme.spacing.md, paddingTop: theme.spacing.md, borderTopWidth: 1, borderTopColor: theme.colors.border, }, debugTitle: { fontSize: 16, color: theme.colors.textPrimary, fontFamily: theme.typography.fontFamily.bold, marginBottom: theme.spacing.sm, }, debugText: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.regular, textAlign: 'center', paddingHorizontal: theme.spacing.md, lineHeight: 20, }, tabContainer: { flexDirection: 'row', justifyContent: 'space-around', paddingVertical: theme.spacing.sm, paddingHorizontal: theme.spacing.md, borderBottomWidth: 1, borderBottomColor: theme.colors.border, backgroundColor: theme.colors.background, }, tabButton: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: theme.spacing.md, paddingVertical: theme.spacing.sm, borderRadius: 12, borderWidth: 1, borderColor: theme.colors.border, minWidth: 110, justifyContent: 'center', position: 'relative', backgroundColor: theme.colors.backgroundAlt, }, tabButtonActive: { borderColor: theme.colors.primary, backgroundColor: theme.colors.primary, shadowColor: theme.colors.primary, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, elevation: 3, }, tabButtonText: { fontSize: 14, color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily.medium, marginLeft: theme.spacing.xs, }, tabButtonTextActive: { color: theme.colors.background, fontFamily: theme.typography.fontFamily.bold, }, tabIndicator: { position: 'absolute', bottom: -theme.spacing.sm, left: 0, right: 0, height: 3, backgroundColor: theme.colors.primary, borderRadius: 2, }, }); export default SeriesDetailScreen;