diff --git a/android/app/src/main/assets/fonts/Roboto-Black.ttf b/android/app/src/main/assets/fonts/Roboto-Black.ttf deleted file mode 100644 index d51221a..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-Black.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-Bold.ttf b/android/app/src/main/assets/fonts/Roboto-Bold.ttf deleted file mode 100644 index 9d7cf22..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-Bold.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf b/android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf deleted file mode 100644 index 7092a88..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf b/android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf deleted file mode 100644 index 75608c6..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-Light.ttf b/android/app/src/main/assets/fonts/Roboto-Light.ttf deleted file mode 100644 index 6fcd5f9..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-Light.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-Medium.ttf b/android/app/src/main/assets/fonts/Roboto-Medium.ttf deleted file mode 100644 index d629e98..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-Medium.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-Regular.ttf b/android/app/src/main/assets/fonts/Roboto-Regular.ttf deleted file mode 100644 index 7e3bb2f..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-Regular.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/Roboto-SemiBold.ttf b/android/app/src/main/assets/fonts/Roboto-SemiBold.ttf deleted file mode 100644 index 3f34834..0000000 Binary files a/android/app/src/main/assets/fonts/Roboto-SemiBold.ttf and /dev/null differ diff --git a/android/app/src/main/assets/fonts/WorkSans-Bold.ttf b/android/app/src/main/assets/fonts/WorkSans-Bold.ttf new file mode 100644 index 0000000..9542d67 Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-Bold.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-ExtraBold.ttf b/android/app/src/main/assets/fonts/WorkSans-ExtraBold.ttf new file mode 100644 index 0000000..6dc6fca Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-ExtraBold.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-ExtraLight.ttf b/android/app/src/main/assets/fonts/WorkSans-ExtraLight.ttf new file mode 100644 index 0000000..a6d4617 Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-ExtraLight.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-Light.ttf b/android/app/src/main/assets/fonts/WorkSans-Light.ttf new file mode 100644 index 0000000..1fe1fd1 Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-Light.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-Medium.ttf b/android/app/src/main/assets/fonts/WorkSans-Medium.ttf new file mode 100644 index 0000000..2168073 Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-Medium.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-Regular.ttf b/android/app/src/main/assets/fonts/WorkSans-Regular.ttf new file mode 100644 index 0000000..d24586c Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-Regular.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-SemiBold.ttf b/android/app/src/main/assets/fonts/WorkSans-SemiBold.ttf new file mode 100644 index 0000000..a75721c Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-SemiBold.ttf differ diff --git a/android/app/src/main/assets/fonts/WorkSans-Thin.ttf b/android/app/src/main/assets/fonts/WorkSans-Thin.ttf new file mode 100644 index 0000000..a097d7c Binary files /dev/null and b/android/app/src/main/assets/fonts/WorkSans-Thin.ttf differ diff --git a/android/link-assets-manifest.json b/android/link-assets-manifest.json index 441aa21..5d7736d 100644 --- a/android/link-assets-manifest.json +++ b/android/link-assets-manifest.json @@ -2,36 +2,36 @@ "migIndex": 1, "data": [ { - "path": "app/assets/fonts/Roboto-Black.ttf", - "sha1": "d1678489a8d5645f16486ec52d77b651ff0bf327" + "path": "app/assets/fonts/WorkSans-Bold.ttf", + "sha1": "ec84061651ead3c3c5cbb61c2d338aca0bacdc1e" }, { - "path": "app/assets/fonts/Roboto-Bold.ttf", - "sha1": "508c35dee818addce6cc6d1fb6e42f039da5a7cf" + "path": "app/assets/fonts/WorkSans-ExtraBold.ttf", + "sha1": "0b371d1dbfbdd15db880bbd129b239530c71accb" }, { - "path": "app/assets/fonts/Roboto-ExtraBold.ttf", - "sha1": "3dbfd71b6fbcfbd8e7ee8a8dd033dc5aaad63249" + "path": "app/assets/fonts/WorkSans-ExtraLight.ttf", + "sha1": "74596e55487e2961b6c43993698d658e2ceee77b" }, { - "path": "app/assets/fonts/Roboto-ExtraLight.ttf", - "sha1": "df556e64732e5c272349e13cb5f87591a1ae779b" + "path": "app/assets/fonts/WorkSans-Light.ttf", + "sha1": "293e11dae7e8b930bf5eea0b06ca979531f22189" }, { - "path": "app/assets/fonts/Roboto-Light.ttf", - "sha1": "318b44c0a32848f78bf11d4fbf3355d00647a796" + "path": "app/assets/fonts/WorkSans-Medium.ttf", + "sha1": "c281f8454dd193c2260e43ae2de171c5dd4086e4" }, { - "path": "app/assets/fonts/Roboto-Medium.ttf", - "sha1": "fa5192203f85ddb667579e1bdf26f12098bb873b" + "path": "app/assets/fonts/WorkSans-Regular.ttf", + "sha1": "5e0183b29b57c54595c62ac6bc223b21f1434226" }, { - "path": "app/assets/fonts/Roboto-Regular.ttf", - "sha1": "3bff51436aa7eb995d84cfc592cc63e1316bb400" + "path": "app/assets/fonts/WorkSans-SemiBold.ttf", + "sha1": "64b8fe156fafce221a0f66504255257053fc6062" }, { - "path": "app/assets/fonts/Roboto-SemiBold.ttf", - "sha1": "9ca139684fe902c8310dd82991648376ac9838db" + "path": "app/assets/fonts/WorkSans-Thin.ttf", + "sha1": "a62251331038fdd079c47bc413a350efbf702db8" } ] } diff --git a/app/assets/fonts/Roboto-Black.ttf b/app/assets/fonts/Roboto-Black.ttf deleted file mode 100644 index d51221a..0000000 Binary files a/app/assets/fonts/Roboto-Black.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-Bold.ttf b/app/assets/fonts/Roboto-Bold.ttf deleted file mode 100644 index 9d7cf22..0000000 Binary files a/app/assets/fonts/Roboto-Bold.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-ExtraBold.ttf b/app/assets/fonts/Roboto-ExtraBold.ttf deleted file mode 100644 index 7092a88..0000000 Binary files a/app/assets/fonts/Roboto-ExtraBold.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-ExtraLight.ttf b/app/assets/fonts/Roboto-ExtraLight.ttf deleted file mode 100644 index 75608c6..0000000 Binary files a/app/assets/fonts/Roboto-ExtraLight.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-Light.ttf b/app/assets/fonts/Roboto-Light.ttf deleted file mode 100644 index 6fcd5f9..0000000 Binary files a/app/assets/fonts/Roboto-Light.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-Medium.ttf b/app/assets/fonts/Roboto-Medium.ttf deleted file mode 100644 index d629e98..0000000 Binary files a/app/assets/fonts/Roboto-Medium.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-Regular.ttf b/app/assets/fonts/Roboto-Regular.ttf deleted file mode 100644 index 7e3bb2f..0000000 Binary files a/app/assets/fonts/Roboto-Regular.ttf and /dev/null differ diff --git a/app/assets/fonts/Roboto-SemiBold.ttf b/app/assets/fonts/Roboto-SemiBold.ttf deleted file mode 100644 index 3f34834..0000000 Binary files a/app/assets/fonts/Roboto-SemiBold.ttf and /dev/null differ diff --git a/app/assets/fonts/WorkSans-Bold.ttf b/app/assets/fonts/WorkSans-Bold.ttf new file mode 100644 index 0000000..9542d67 Binary files /dev/null and b/app/assets/fonts/WorkSans-Bold.ttf differ diff --git a/app/assets/fonts/WorkSans-ExtraBold.ttf b/app/assets/fonts/WorkSans-ExtraBold.ttf new file mode 100644 index 0000000..6dc6fca Binary files /dev/null and b/app/assets/fonts/WorkSans-ExtraBold.ttf differ diff --git a/app/assets/fonts/WorkSans-ExtraLight.ttf b/app/assets/fonts/WorkSans-ExtraLight.ttf new file mode 100644 index 0000000..a6d4617 Binary files /dev/null and b/app/assets/fonts/WorkSans-ExtraLight.ttf differ diff --git a/app/assets/fonts/WorkSans-Light.ttf b/app/assets/fonts/WorkSans-Light.ttf new file mode 100644 index 0000000..1fe1fd1 Binary files /dev/null and b/app/assets/fonts/WorkSans-Light.ttf differ diff --git a/app/assets/fonts/WorkSans-Medium.ttf b/app/assets/fonts/WorkSans-Medium.ttf new file mode 100644 index 0000000..2168073 Binary files /dev/null and b/app/assets/fonts/WorkSans-Medium.ttf differ diff --git a/app/assets/fonts/WorkSans-Regular.ttf b/app/assets/fonts/WorkSans-Regular.ttf new file mode 100644 index 0000000..d24586c Binary files /dev/null and b/app/assets/fonts/WorkSans-Regular.ttf differ diff --git a/app/assets/fonts/WorkSans-SemiBold.ttf b/app/assets/fonts/WorkSans-SemiBold.ttf new file mode 100644 index 0000000..a75721c Binary files /dev/null and b/app/assets/fonts/WorkSans-SemiBold.ttf differ diff --git a/app/assets/fonts/WorkSans-Thin.ttf b/app/assets/fonts/WorkSans-Thin.ttf new file mode 100644 index 0000000..a097d7c Binary files /dev/null and b/app/assets/fonts/WorkSans-Thin.ttf differ diff --git a/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx b/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx index 87474af..06d3be5 100644 --- a/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx +++ b/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx @@ -13,11 +13,13 @@ import { ScrollView, TextInput, TouchableOpacity, + TouchableWithoutFeedback, Alert, SafeAreaView, StatusBar, KeyboardAvoidingView, Platform, + Dimensions, } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; import Icon from 'react-native-vector-icons/Feather'; @@ -36,6 +38,9 @@ import { selectUser } from '../../Auth/redux/authSelectors'; import type { AIPredictionDetailsScreenProps } from '../navigation/navigationTypes'; import type { AIPredictionCase } from '../types'; +// Get screen dimensions +const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); + // ============================================================================ // ENUMS & TYPES // ============================================================================ @@ -118,19 +123,17 @@ interface APIResponse { // ============================================================================ const SUGGESTION_TYPE_OPTIONS = [ - { label: 'Treatment', value: SuggestionType.TREATMENT }, - { label: 'Follow Up', value: SuggestionType.FOLLOW_UP }, - { label: 'Diagnosis', value: SuggestionType.DIAGNOSIS }, - { label: 'Other', value: SuggestionType.OTHER }, + { label: 'Treatment', value: SuggestionType.TREATMENT, icon: 'activity', color: '#4CAF50' }, + { label: 'Follow Up', value: SuggestionType.FOLLOW_UP, icon: 'calendar', color: '#2196F3' }, + { label: 'Diagnosis', value: SuggestionType.DIAGNOSIS, icon: 'search', color: '#FF9800' }, + { label: 'Other', value: SuggestionType.OTHER, icon: 'more-horizontal', color: '#9C27B0' }, ]; - - const PRIORITY_OPTIONS = [ - { label: 'Low', value: Priority.LOW, color: theme.colors.success }, - { label: 'Medium', value: Priority.MEDIUM, color: theme.colors.warning }, - { label: 'High', value: Priority.HIGH, color: '#FF5722' }, - { label: 'Critical', value: Priority.CRITICAL, color: theme.colors.critical }, + { label: 'Low', value: Priority.LOW, color: '#4CAF50', bgColor: '#E8F5E8' }, + { label: 'Medium', value: Priority.MEDIUM, color: '#FF9800', bgColor: '#FFF3E0' }, + { label: 'High', value: Priority.HIGH, color: '#FF5722', bgColor: '#FFEBEE' }, + { label: 'Critical', value: Priority.CRITICAL, color: '#F44336', bgColor: '#FFEBEE' }, ]; // ============================================================================ @@ -150,6 +153,7 @@ const PRIORITY_OPTIONS = [ * - Date picker for expiration * - Dropdown selections for types and priorities * - Responsive design with keyboard handling + * - Enhanced visual design with modern mobile styling */ const AIPredictionDetailScreen: React.FC = ({ navigation, @@ -201,6 +205,7 @@ const AIPredictionDetailScreen: React.FC = ({ const [showSuccessModal, setShowSuccessModal] = useState(false); const [apiResponse, setApiResponse] = useState(null); + // ============================================================================ // EFFECTS // ============================================================================ @@ -246,6 +251,14 @@ const AIPredictionDetailScreen: React.FC = ({ setShowPriorityDropdown(false); }, []); + /** + * Close all dropdowns + */ + const closeAllDropdowns = useCallback(() => { + setShowSuggestionTypeDropdown(false); + setShowPriorityDropdown(false); + }, []); + /** * Handle date selection */ @@ -408,15 +421,33 @@ const AIPredictionDetailScreen: React.FC = ({ navigation.goBack(); }, [navigation]); + + // ============================================================================ // RENDER FUNCTIONS // ============================================================================ /** - * Render dropdown options + * Render enhanced header + */ + const renderHeader = () => ( + + + + + + AI Prediction Details + Create Medical Suggestion + + + + ); + + /** + * Render enhanced dropdown options */ const renderDropdownOptions = useCallback(( - options: Array<{ label: string; value: any; color?: string }>, + options: Array<{ label: string; value: any; color?: string; bgColor?: string; icon?: string }>, currentValue: any, onSelect: (value: any) => void ) => ( @@ -430,13 +461,23 @@ const AIPredictionDetailScreen: React.FC = ({ ]} onPress={() => onSelect(option.value)} > - - {option.label} - + + {option.icon && ( + + )} + + {option.label} + + {currentValue === option.value && ( )} @@ -446,43 +487,51 @@ const AIPredictionDetailScreen: React.FC = ({ ), []); /** - * Render related findings section + * Render enhanced related findings section */ const renderRelatedFindings = useCallback(() => ( - Related Findings - {formData.relatedFindings.map((finding, index) => ( - - handleUpdateRelatedFinding(index, 'key', value)} - placeholder="Key" - placeholderTextColor={theme.colors.textMuted} - /> - handleUpdateRelatedFinding(index, 'value', value)} - placeholder="Value" - placeholderTextColor={theme.colors.textMuted} - /> - handleRemoveRelatedFinding(index)} - accessibilityRole="button" - accessibilityLabel="Remove related finding" - > - - - - ))} - - Add - + + + Related Findings + + + {formData.relatedFindings.map((finding, index) => ( + + + handleUpdateRelatedFinding(index, 'key', value)} + placeholder="Finding Key" + placeholderTextColor={theme.colors.textMuted} + /> + handleUpdateRelatedFinding(index, 'value', value)} + placeholder="Finding Value" + placeholderTextColor={theme.colors.textMuted} + /> + + handleRemoveRelatedFinding(index)} + accessibilityRole="button" + accessibilityLabel="Remove related finding" + > + + + + ))} + + + Add Finding + + ), [formData.relatedFindings, handleUpdateRelatedFinding, handleRemoveRelatedFinding, handleAddRelatedFinding]); @@ -494,275 +543,362 @@ const AIPredictionDetailScreen: React.FC = ({ - {/* Header */} + + <> + {/* Enhanced Header */} + {renderHeader()} - - + - {/* Patient ID */} - - Patient ID - - - - {/* Suggestion Type */} - - Suggestion Type - - setShowSuggestionTypeDropdown(!showSuggestionTypeDropdown)} - > - {formData.suggestionType} - - - {showSuggestionTypeDropdown && renderDropdownOptions( - SUGGESTION_TYPE_OPTIONS, - formData.suggestionType, - (value) => handleDropdownSelect('suggestionType', value) - )} + + {/* Patient ID Card */} + + + + Patient Information - - - {/* Title */} - - Title - handleFieldChange('title', value)} - placeholder="Recommended CT Scan" - placeholderTextColor={theme.colors.textMuted} - /> - - - {/* Suggestion Text */} - - Suggestion Text - handleFieldChange('suggestionText', value)} - placeholder="Describe your suggestion with clinical reasoning..." - placeholderTextColor={theme.colors.textMuted} - multiline - numberOfLines={4} - textAlignVertical="top" - /> - - - {/* Row 1: Confidence and Priority */} - - - Confidence (0-1) + + Patient ID handleFieldChange('confidence', value)} - placeholder="0.9979" + style={[styles.input, styles.disabledInput]} + value={formData.patientId} + editable={false} + placeholder="Patient ID" placeholderTextColor={theme.colors.textMuted} - keyboardType="numeric" /> + - - Priority + {/* Suggestion Type Card */} + + + + Suggestion Type + + + Type - setShowPriorityDropdown(!showPriorityDropdown)} - > - {formData.priority} + setShowSuggestionTypeDropdown(!showSuggestionTypeDropdown)} + > + + opt.value === formData.suggestionType)?.icon as any || 'edit-3'} + size={18} + color={SUGGESTION_TYPE_OPTIONS.find(opt => opt.value === formData.suggestionType)?.color || theme.colors.primary} + /> + {formData.suggestionType} + - {showPriorityDropdown && renderDropdownOptions( - PRIORITY_OPTIONS, - formData.priority, - (value) => handleDropdownSelect('priority', value) - )} + {showSuggestionTypeDropdown && renderDropdownOptions( + SUGGESTION_TYPE_OPTIONS, + formData.suggestionType, + (value) => handleDropdownSelect('suggestionType', value) + )} - {/* Row 2: Category and Cost Estimate */} - - - Category + {/* Main Suggestion Card */} + + + + Suggestion Details + + + {/* Title */} + + Title handleFieldChange('category', value)} - placeholder="Radiology" + style={styles.enhancedInput} + value={formData.title} + onChangeText={(value) => handleFieldChange('title', value)} + placeholder="Recommended CT Scan" placeholderTextColor={theme.colors.textMuted} /> - - Cost Estimate (USD) + {/* Suggestion Text */} + + Suggestion Text handleFieldChange('costEstimate', value)} - placeholder="Enter cost" + style={[styles.enhancedInput, styles.textArea]} + value={formData.suggestionText} + onChangeText={(value) => handleFieldChange('suggestionText', value)} + placeholder="Describe your suggestion with clinical reasoning..." placeholderTextColor={theme.colors.textMuted} - keyboardType="numeric" + multiline + numberOfLines={4} + textAlignVertical="top" /> - {/* Row 3: Time Estimate and Expires At */} - - - Time Estimate + {/* Confidence & Priority Card */} + + + + Assessment & Priority + + + {/* Row 1: Confidence and Priority */} + + + Confidence (0-1) + handleFieldChange('confidence', value)} + placeholder="0.9979" + placeholderTextColor={theme.colors.textMuted} + keyboardType="numeric" + /> + + + + Priority + + setShowPriorityDropdown(!showPriorityDropdown)} + > + + opt.value === formData.priority)?.bgColor || theme.colors.backgroundAlt } + ]} /> + {formData.priority} + + + + {showPriorityDropdown && renderDropdownOptions( + PRIORITY_OPTIONS, + formData.priority, + (value) => handleDropdownSelect('priority', value) + )} + + + + + + {/* Category & Cost Card */} + + + + Classification & Resources + + + {/* Row 2: Category and Cost Estimate */} + + + Category + handleFieldChange('category', value)} + placeholder="Radiology" + placeholderTextColor={theme.colors.textMuted} + /> + + + + Cost Estimate (USD) + handleFieldChange('costEstimate', value)} + placeholder="Enter cost" + placeholderTextColor={theme.colors.textMuted} + keyboardType="numeric" + /> + + + + + {/* Time & Expiry Card */} + + + + Timeline + + + {/* Row 3: Time Estimate and Expires At */} + + + Time Estimate + handleFieldChange('timeEstimate', value)} + placeholder="1-2 hours" + placeholderTextColor={theme.colors.textMuted} + /> + + + + Expires At + setShowDatePicker(true)} + > + + + {formData.expiresAt + ? formData.expiresAt.toLocaleDateString() + : 'Select date' + } + + + Date must be in the future + + + + + {/* AI Model & Evidence Card */} + + + + AI Model & Evidence + + + {/* AI Model Version */} + + AI Model Version handleFieldChange('timeEstimate', value)} - placeholder="1-2 hours" + style={styles.enhancedInput} + value={formData.aiModelVersion} + onChangeText={(value) => handleFieldChange('aiModelVersion', value)} + placeholder="v2.1.0" placeholderTextColor={theme.colors.textMuted} /> - - Expires At - setShowDatePicker(true)} - > - - {formData.expiresAt - ? formData.expiresAt.toLocaleDateString() - : 'Select date' - } - - - - Date must be in the future - + {/* Evidence Sources */} + + Evidence Sources (comma-separated) + handleFieldChange('evidenceSources', value)} + placeholder="Evidence A, Protocol B" + placeholderTextColor={theme.colors.textMuted} + /> + - {/* AI Model Version */} - - AI Model Version - handleFieldChange('aiModelVersion', value)} - placeholder="v2.1.0" - placeholderTextColor={theme.colors.textMuted} - /> - + {/* Contraindications & Tags Card */} + + + + Safety & Organization + + + {/* Contraindications */} + + Contraindications + handleFieldChange('contraindications', value)} + placeholder="Any known contraindications..." + placeholderTextColor={theme.colors.textMuted} + multiline + numberOfLines={3} + textAlignVertical="top" + /> + - {/* Evidence Sources */} - - Evidence Sources (comma-separated) - handleFieldChange('evidenceSources', value)} - placeholder="Evidence A, Protocol B" - placeholderTextColor={theme.colors.textMuted} - /> - - - {/* Contraindications */} - - Contraindications - handleFieldChange('contraindications', value)} - placeholder="Any known contraindications..." - placeholderTextColor={theme.colors.textMuted} - multiline - numberOfLines={3} - textAlignVertical="top" - /> - - - {/* Tags */} - - Tags (comma-separated) - handleFieldChange('tags', value)} - placeholder="emergency, chest, pulmonary" - placeholderTextColor={theme.colors.textMuted} - /> + {/* Tags */} + + Tags (comma-separated) + handleFieldChange('tags', value)} + placeholder="emergency, chest, pulmonary" + placeholderTextColor={theme.colors.textMuted} + /> + {/* Related Findings */} {renderRelatedFindings()} - {/* Submit Button */} - - {isSubmitting ? ( - Submitting... - ) : ( - Submit Suggestion - )} - - - + {/* Submit Button */} + + {isSubmitting ? ( + + + Submitting... + + ) : ( + + + Submit Suggestion + + )} + + + + + - {/* Date Picker */} - {showDatePicker && ( - - )} + - {/* Success Modal */} - - - {/* Toast Messages */} - - - ); - }; + {/* Date Picker */} + {showDatePicker && ( + + )} + + {/* Success Modal */} + + + {/* Toast Messages */} + + + ); +}; // ============================================================================ // STYLES @@ -771,41 +907,113 @@ const AIPredictionDetailScreen: React.FC = ({ const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: theme.colors.background, + backgroundColor: '#F8FAFC', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.md, - borderBottomWidth: 1, - borderBottomColor: theme.colors.border, + paddingVertical: theme.spacing.lg, backgroundColor: theme.colors.background, + borderBottomWidth: 1, + borderBottomColor: '#E2E8F0', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.06, + shadowRadius: 2, + elevation: 2, }, backButton: { padding: theme.spacing.sm, + backgroundColor: '#F1F5F9', + borderRadius: theme.borderRadius.medium, + }, + headerContent: { + flex: 1, + alignItems: 'center', }, headerTitle: { fontSize: theme.typography.fontSize.displaySmall, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.textPrimary, + marginBottom: 2, + }, + headerSubtitle: { + fontSize: theme.typography.fontSize.bodySmall, + fontFamily: theme.typography.fontFamily.regular, + color: theme.colors.textSecondary, }, headerSpacer: { width: 40, }, - keyboardAvoidingView: { - flex: 1, - // Ensure this doesn't interfere with bottom tab navigation - position: 'relative', - }, + keyboardAvoidingView: { + flex: 1, + position: 'relative', + }, scrollView: { flex: 1, }, - scrollContent: { - padding: theme.spacing.md, - paddingBottom: theme.spacing.xl, + scrollContent: { + padding: theme.spacing.md, + paddingBottom: theme.spacing.xl, + }, + mainContent: { + flex: 1, + }, + + // Card Styles + patientCard: { + backgroundColor: theme.colors.background, + borderRadius: theme.borderRadius.large, + padding: theme.spacing.lg, + marginBottom: theme.spacing.lg, + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + borderLeftWidth: 4, + borderLeftColor: theme.colors.primary, }, + formCard: { + backgroundColor: theme.colors.background, + borderRadius: theme.borderRadius.large, + padding: theme.spacing.lg, + marginBottom: theme.spacing.lg, + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + }, + cardHeader: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: theme.spacing.md, + paddingBottom: theme.spacing.sm, + borderBottomWidth: 1, + borderBottomColor: '#E2E8F0', + }, + cardTitle: { + fontSize: theme.typography.fontSize.bodyLarge, + fontFamily: theme.typography.fontFamily.bold, + color: theme.colors.textPrimary, + marginLeft: theme.spacing.sm, + }, + patientCardHeader: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: theme.spacing.md, + }, + patientCardTitle: { + fontSize: theme.typography.fontSize.bodyLarge, + fontFamily: theme.typography.fontFamily.bold, + color: theme.colors.textPrimary, + marginLeft: theme.spacing.sm, + }, + + // Form Styles formGroup: { marginBottom: theme.spacing.md, }, @@ -833,15 +1041,33 @@ const styles = StyleSheet.create({ color: theme.colors.textPrimary, backgroundColor: theme.colors.background, }, + enhancedInput: { + borderWidth: 1, + borderColor: '#E2E8F0', + borderRadius: theme.borderRadius.medium, + paddingHorizontal: theme.spacing.md, + paddingVertical: theme.spacing.md, + fontSize: theme.typography.fontSize.bodyMedium, + fontFamily: theme.typography.fontFamily.regular, + color: theme.colors.textPrimary, + backgroundColor: '#FFFFFF', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.03, + shadowRadius: 1, + elevation: 1, + }, disabledInput: { - backgroundColor: theme.colors.backgroundAlt, + backgroundColor: '#F8FAFC', color: theme.colors.textSecondary, - borderColor: theme.colors.border, + borderColor: '#E2E8F0', }, textArea: { - height: 80, + height: 100, textAlignVertical: 'top', }, + + // Dropdown Styles dropdownContainer: { position: 'relative', }, @@ -856,35 +1082,69 @@ const styles = StyleSheet.create({ paddingVertical: theme.spacing.sm, backgroundColor: theme.colors.background, }, + enhancedDropdown: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderWidth: 1, + borderColor: '#E2E8F0', + borderRadius: theme.borderRadius.medium, + paddingHorizontal: theme.spacing.md, + paddingVertical: theme.spacing.md, + backgroundColor: '#FFFFFF', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.03, + shadowRadius: 1, + elevation: 1, + }, + dropdownContent: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }, dropdownText: { fontSize: theme.typography.fontSize.bodyMedium, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textPrimary, + marginLeft: theme.spacing.sm, }, + dropdownOptions: { position: 'absolute', top: '100%', left: 0, right: 0, - zIndex: 1000, borderWidth: 1, - borderColor: theme.colors.border, + borderColor: '#E2E8F0', borderRadius: theme.borderRadius.medium, - backgroundColor: theme.colors.background, + backgroundColor: '#FFFFFF', marginTop: theme.spacing.xs, - ...theme.shadows.medium, + shadowColor: '#000000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, }, dropdownOption: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, + paddingVertical: theme.spacing.md, borderBottomWidth: 1, - borderBottomColor: theme.colors.border, + borderBottomColor: '#F1F5F9', + }, + dropdownOptionContent: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }, + dropdownOptionIcon: { + marginRight: theme.spacing.sm, }, dropdownOptionSelected: { - backgroundColor: theme.colors.tertiary, + backgroundColor: '#F8FAFC', }, dropdownOptionText: { fontSize: theme.typography.fontSize.bodyMedium, @@ -895,6 +1155,14 @@ const styles = StyleSheet.create({ color: theme.colors.primary, fontFamily: theme.typography.fontFamily.medium, }, + priorityIndicator: { + width: 12, + height: 12, + borderRadius: 6, + marginRight: theme.spacing.sm, + }, + + // Date Input Styles dateInput: { flexDirection: 'row', alignItems: 'center', @@ -906,29 +1174,67 @@ const styles = StyleSheet.create({ paddingVertical: theme.spacing.sm, backgroundColor: theme.colors.background, }, + enhancedDateInput: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderWidth: 1, + borderColor: '#E2E8F0', + borderRadius: theme.borderRadius.medium, + paddingHorizontal: theme.spacing.md, + paddingVertical: theme.spacing.md, + backgroundColor: '#FFFFFF', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.03, + shadowRadius: 1, + elevation: 1, + }, dateText: { fontSize: theme.typography.fontSize.bodyMedium, fontFamily: theme.typography.fontFamily.regular, color: theme.colors.textPrimary, + flex: 1, + marginLeft: theme.spacing.sm, + }, + placeholderText: { + color: theme.colors.textMuted, + }, + helperText: { + fontSize: theme.typography.fontSize.bodySmall, + fontFamily: theme.typography.fontFamily.regular, + color: theme.colors.textMuted, + marginTop: theme.spacing.xs, + fontStyle: 'italic', + }, + + // Section Styles + sectionHeader: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: theme.spacing.md, }, - placeholderText: { - color: theme.colors.textMuted, - }, - helperText: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textMuted, - marginTop: theme.spacing.xs, - fontStyle: 'italic', - }, sectionTitle: { fontSize: theme.typography.fontSize.bodyLarge, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.textPrimary, - marginBottom: theme.spacing.md, + marginLeft: theme.spacing.sm, }, + + // Related Findings Styles relatedFindingsContainer: { + backgroundColor: theme.colors.background, + borderRadius: theme.borderRadius.large, + padding: theme.spacing.lg, marginBottom: theme.spacing.lg, + shadowColor: '#000000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + }, + relatedFindingsContent: { + marginTop: theme.spacing.sm, }, relatedFindingItem: { flexDirection: 'row', @@ -936,11 +1242,20 @@ const styles = StyleSheet.create({ gap: theme.spacing.sm, marginBottom: theme.spacing.sm, }, + relatedFindingInputs: { + flex: 1, + flexDirection: 'row', + gap: theme.spacing.sm, + }, relatedFindingKey: { flex: 1, + backgroundColor: '#F8FAFC', + borderColor: '#E2E8F0', }, relatedFindingValue: { flex: 1, + backgroundColor: '#F8FAFC', + borderColor: '#E2E8F0', }, addButton: { backgroundColor: theme.colors.primary, @@ -949,6 +1264,13 @@ const styles = StyleSheet.create({ paddingVertical: theme.spacing.sm, alignItems: 'center', alignSelf: 'flex-start', + flexDirection: 'row', + gap: theme.spacing.sm, + shadowColor: theme.colors.primary, + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.2, + shadowRadius: 2, + elevation: 2, }, addButtonText: { fontSize: theme.typography.fontSize.bodyMedium, @@ -956,26 +1278,43 @@ const styles = StyleSheet.create({ color: theme.colors.background, }, removeButton: { - padding: theme.spacing.xs, + padding: theme.spacing.sm, justifyContent: 'center', alignItems: 'center', + backgroundColor: '#FEF2F2', + borderRadius: theme.borderRadius.medium, }, + + // Submit Button Styles submitButton: { backgroundColor: theme.colors.primary, - borderRadius: theme.borderRadius.medium, - paddingVertical: theme.spacing.md, + borderRadius: theme.borderRadius.large, + paddingVertical: theme.spacing.lg, alignItems: 'center', - marginTop: theme.spacing.lg, - ...theme.shadows.medium, + marginTop: theme.spacing.xl, + shadowColor: theme.colors.primary, + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 4, + elevation: 4, }, submitButtonDisabled: { backgroundColor: theme.colors.textMuted, + shadowOpacity: 0.1, + }, + submitButtonContent: { + flexDirection: 'row', + alignItems: 'center', + gap: theme.spacing.sm, }, submitButtonText: { fontSize: theme.typography.fontSize.bodyLarge, fontFamily: theme.typography.fontFamily.bold, color: theme.colors.background, }, + spinningIcon: { + // Add rotation animation if needed + }, }); export default AIPredictionDetailScreen; diff --git a/app/modules/Dashboard/components/BrainPredictionsOverview.tsx b/app/modules/Dashboard/components/BrainPredictionsOverview.tsx index 40e2643..f124c79 100644 --- a/app/modules/Dashboard/components/BrainPredictionsOverview.tsx +++ b/app/modules/Dashboard/components/BrainPredictionsOverview.tsx @@ -365,7 +365,7 @@ const styles = StyleSheet.create({ // Header section header: { - marginBottom: theme.spacing.lg, + // marginBottom: theme.spacing.lg, }, // Main title @@ -465,10 +465,8 @@ const styles = StyleSheet.create({ // Pie chart container pieChartContainer: { alignItems: 'center', - marginBottom: theme.spacing.lg, backgroundColor: theme.colors.background, borderRadius: theme.borderRadius.medium, - padding: theme.spacing.md, }, // Legend container @@ -478,7 +476,6 @@ const styles = StyleSheet.create({ backgroundColor: theme.colors.backgroundAlt, borderRadius: theme.borderRadius.medium, padding: theme.spacing.md, - marginTop: theme.spacing.md, }, // Legend title diff --git a/app/modules/PatientCare/components/ImageViewer.tsx b/app/modules/PatientCare/components/ImageViewer.tsx new file mode 100644 index 0000000..3f4f634 --- /dev/null +++ b/app/modules/PatientCare/components/ImageViewer.tsx @@ -0,0 +1,503 @@ +/* + * File: ImageViewer.tsx + * Description: Full-screen DICOM image viewer with zoom, pan, and navigation + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ + +import React, { useState, useCallback } from 'react'; +import { + View, + Text, + StyleSheet, + TouchableOpacity, + Dimensions, + Image, + ScrollView, + StatusBar, + SafeAreaView, +} from 'react-native'; +import { theme } from '../../../theme/theme'; +import Icon from 'react-native-vector-icons/Feather'; +import { API_CONFIG } from '../../../shared/utils'; + +// Get screen dimensions +const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); + +// ============================================================================ +// INTERFACES +// ============================================================================ + +interface ImageViewerProps { + visible: boolean; + images: string[]; + initialIndex: number; + onClose: () => void; + patientName?: string; + seriesInfo?: string; +} + +// ============================================================================ +// IMAGE VIEWER COMPONENT +// ============================================================================ + +/** + * ImageViewer Component + * + * Purpose: Full-screen DICOM image viewer with advanced viewing capabilities + * + * Features: + * - Full-screen image display + * - Image navigation (previous/next) + * - Zoom and pan functionality + * - Patient information display + * - Series information + * - Touch gestures for navigation + * - Professional medical imaging interface + */ +const ImageViewer: React.FC = ({ + visible, + images, + initialIndex, + onClose, + patientName = 'Unknown Patient', + seriesInfo = 'DICOM Series', +}) => { + // ============================================================================ + // STATE MANAGEMENT + // ============================================================================ + + const [currentIndex, setCurrentIndex] = useState(initialIndex); + const [scale, setScale] = useState(1); + const [isZoomed, setIsZoomed] = useState(false); + + // ============================================================================ + // EVENT HANDLERS + // ============================================================================ + + /** + * Handle Previous Image + * + * Purpose: Navigate to previous image in series + */ + const handlePrevious = useCallback(() => { + if (currentIndex > 0) { + setCurrentIndex(currentIndex - 1); + setScale(1); + setIsZoomed(false); + } + }, [currentIndex]); + + /** + * Handle Next Image + * + * Purpose: Navigate to next image in series + */ + const handleNext = useCallback(() => { + if (currentIndex < images.length - 1) { + setCurrentIndex(currentIndex + 1); + setScale(1); + setIsZoomed(false); + } + }, [currentIndex, images.length]); + + /** + * Handle Zoom In + * + * Purpose: Increase image zoom level + */ + const handleZoomIn = useCallback(() => { + const newScale = Math.min(scale * 1.5, 3); + setScale(newScale); + setIsZoomed(newScale > 1); + }, [scale]); + + /** + * Handle Zoom Out + * + * Purpose: Decrease image zoom level + */ + const handleZoomOut = useCallback(() => { + const newScale = Math.max(scale / 1.5, 0.5); + setScale(newScale); + setIsZoomed(newScale > 1); + }, [scale]); + + /** + * Handle Reset Zoom + * + * Purpose: Reset image to original size + */ + const handleResetZoom = useCallback(() => { + setScale(1); + setIsZoomed(false); + }, []); + + /** + * Handle Close + * + * Purpose: Close image viewer and return to previous screen + */ + const handleClose = useCallback(() => { + setScale(1); + setIsZoomed(false); + setCurrentIndex(initialIndex); + onClose(); + }, [initialIndex, onClose]); + + // ============================================================================ + // RENDER HELPERS + // ============================================================================ + + /** + * Render Header + * + * Purpose: Render image viewer header with patient info and controls + */ + const renderHeader = () => ( + + + + + + + {patientName} + {seriesInfo} + + + + + + {currentIndex + 1} of {images.length} + + + + ); + + /** + * Render Navigation Controls + * + * Purpose: Render image navigation controls + */ + const renderNavigationControls = () => ( + + + + + + + + + + ); + + /** + * Render Zoom Controls + * + * Purpose: Render zoom control buttons + */ + const renderZoomControls = () => ( + + + + + + + {Math.round(scale * 100)}% + + + + + + + ); + + // ============================================================================ + // MAIN RENDER + // ============================================================================ + + if (!visible || images.length === 0) { + return null; + } + + return ( + + + + {/* Header */} + {renderHeader()} + + {/* Main Image Area */} + + + + + + + {/* Navigation Controls */} + {renderNavigationControls()} + + {/* Zoom Controls */} + {renderZoomControls()} + + {/* Thumbnail Strip */} + + + {images.map((image, index) => ( + setCurrentIndex(index)} + > + + {index === currentIndex && ( + + + + )} + + ))} + + + + ); +}; + +// ============================================================================ +// STYLES +// ============================================================================ + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#000000', + }, + + // Header Styles + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: theme.spacing.md, + paddingVertical: theme.spacing.sm, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + }, + headerLeft: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }, + closeButton: { + padding: theme.spacing.sm, + marginRight: theme.spacing.md, + }, + patientInfo: { + flex: 1, + }, + patientName: { + fontSize: 16, + fontWeight: 'bold', + color: theme.colors.background, + fontFamily: theme.typography.fontFamily.bold, + }, + seriesInfo: { + fontSize: 12, + color: theme.colors.background, + opacity: 0.8, + fontFamily: theme.typography.fontFamily.regular, + }, + headerRight: { + alignItems: 'flex-end', + }, + imageCounter: { + fontSize: 14, + color: theme.colors.background, + fontFamily: theme.typography.fontFamily.medium, + }, + + // Image Container Styles + imageContainer: { + flex: 1, + // backgroundColor: '#000000', + }, + scrollView: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + justifyContent: 'center', + alignItems: 'center', + }, + image: { + width: screenWidth, + height: screenHeight * 0.7, + }, + + // Navigation Controls Styles + navigationControls: { + position: 'absolute', + top: '50%', + left: 0, + right: 0, + flexDirection: 'row', + justifyContent: 'space-between', + paddingHorizontal: theme.spacing.md, + transform: [{ translateY: -20 }], + }, + navButton: { + width: 48, + height: 48, + borderRadius: 24, + backgroundColor: 'rgba(0, 0, 0, 0.6)', + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 4, + elevation: 4, + }, + navButtonDisabled: { + backgroundColor: 'rgba(0, 0, 0, 0.3)', + }, + + // Zoom Controls Styles + zoomControls: { + position: 'absolute', + bottom: 100, + right: theme.spacing.md, + flexDirection: 'column', + alignItems: 'center', + }, + zoomButton: { + width: 44, + height: 44, + borderRadius: 22, + backgroundColor: 'rgba(0, 0, 0, 0.6)', + justifyContent: 'center', + alignItems: 'center', + marginBottom: theme.spacing.sm, + shadowColor: '#000000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 4, + elevation: 4, + }, + zoomText: { + color: theme.colors.background, + fontSize: 12, + fontWeight: 'bold', + fontFamily: theme.typography.fontFamily.bold, + }, + + // Thumbnail Strip Styles + thumbnailStrip: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + paddingVertical: theme.spacing.sm, + }, + thumbnailContent: { + paddingHorizontal: theme.spacing.md, + }, + thumbnail: { + width: 60, + height: 60, + borderRadius: 8, + marginRight: theme.spacing.sm, + position: 'relative', + borderWidth: 2, + borderColor: 'transparent', + }, + activeThumbnail: { + borderColor: theme.colors.primary, + }, + thumbnailImage: { + width: '100%', + height: '100%', + borderRadius: 6, + }, + activeIndicator: { + position: 'absolute', + top: -4, + right: -4, + width: 20, + height: 20, + borderRadius: 10, + backgroundColor: theme.colors.primary, + justifyContent: 'center', + alignItems: 'center', + }, +}); + +export default ImageViewer; + +/* + * End of File: ImageViewer.tsx + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ diff --git a/app/modules/PatientCare/components/PatientCard.tsx b/app/modules/PatientCare/components/PatientCard.tsx index a1627b9..983f4c1 100644 --- a/app/modules/PatientCare/components/PatientCard.tsx +++ b/app/modules/PatientCare/components/PatientCard.tsx @@ -158,7 +158,7 @@ const PatientCard: React.FC = ({ const patientDetails = parseJsonSafely(patient.patientdetails); const patientData = patientDetails.patientdetails || patientDetails; - const series = parseJsonSafely(patient.series); + const series = parseJsonSafely(patientDetails.series); const typeConfig = getCaseTypeConfig(patient.type); // ============================================================================ @@ -207,7 +207,7 @@ const PatientCard: React.FC = ({ (); + +/** + * PatientCareStackNavigator Component + * + * Purpose: Provides stack navigation for PatientCare module + * + * Screens: + * - PatientsScreen: Main patient list screen + * - PatientDetailsScreen: Detailed patient information and DICOM images + * + * Navigation Flow: + * PatientsScreen → PatientDetailsScreen (with patient data) + */ +const PatientCareStackNavigator: React.FC = () => { + return ( + + {/* Patients Screen - Main patient list */} + + + {/* Patient Details Screen - Comprehensive patient information */} + + + ); +}; + +export default PatientCareStackNavigator; + +/* + * End of File: PatientCareStackNavigator.tsx + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ diff --git a/app/modules/PatientCare/navigation/index.ts b/app/modules/PatientCare/navigation/index.ts new file mode 100644 index 0000000..43ffdd9 --- /dev/null +++ b/app/modules/PatientCare/navigation/index.ts @@ -0,0 +1,15 @@ +/* + * File: index.ts + * Description: Barrel export for PatientCare navigation components + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ + +export { default as PatientCareStackNavigator } from './PatientCareStackNavigator'; +export * from './navigationTypes'; + +/* + * End of File: index.ts + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ diff --git a/app/modules/PatientCare/navigation/navigationTypes.ts b/app/modules/PatientCare/navigation/navigationTypes.ts new file mode 100644 index 0000000..aa46fd3 --- /dev/null +++ b/app/modules/PatientCare/navigation/navigationTypes.ts @@ -0,0 +1,118 @@ +/* + * File: navigationTypes.ts + * Description: TypeScript types for PatientCare module navigation + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ + +import { StackNavigationProp } from '@react-navigation/stack'; +import { MedicalCase } from '../../../shared/types'; + +// ============================================================================ +// NAVIGATION PARAMETER LISTS +// ============================================================================ + +/** + * PatientCareStackParamList - Defines the parameter list for PatientCare stack navigator + * + * This interface defines all the screens available in the PatientCare module + * and their associated navigation parameters. + */ +export type PatientCareStackParamList = { + // Patients Screen - Main patient list with search and filtering + PatientsScreen: PatientsScreenParams; + + // Patient Details Screen - Comprehensive patient information and DICOM images + PatientDetails: PatientDetailsScreenParams; +}; + +// ============================================================================ +// SCREEN PARAMETER INTERFACES +// ============================================================================ + +/** + * PatientsScreenParams + * + * Purpose: Parameters for the patients list screen + * + * Parameters: + * - None required - this is the main entry point + */ +export interface PatientsScreenParams { + // No parameters required for main patients screen +} + +/** + * PatientDetailsScreenParams + * + * Purpose: Parameters for the patient details screen + * + * Parameters: + * - patientId: Required patient ID to display details + * - patientName: Required patient name for display + * - medicalCase: Required medical case data with full patient information + */ +export interface PatientDetailsScreenParams { + patientId: string; + patientName: string; + medicalCase: MedicalCase; +} + +// ============================================================================ +// NAVIGATION PROP TYPES +// ============================================================================ + +/** + * PatientCareNavigationProp - Navigation prop type for PatientCare screens + * + * Purpose: Provides type-safe navigation methods for PatientCare module screens + */ +export type PatientCareNavigationProp = StackNavigationProp; + +// ============================================================================ +// SCREEN PROP TYPES +// ============================================================================ + +/** + * PatientsScreenProps - Props for PatientsScreen component + */ +export interface PatientsScreenProps { + navigation: PatientCareNavigationProp; + route: { + params: PatientsScreenParams; + }; +} + +/** + * PatientDetailsScreenProps - Props for PatientDetailsScreen component + */ +export interface PatientDetailsScreenProps { + navigation: PatientCareNavigationProp; + route: { + params: PatientDetailsScreenParams; + }; +} + +// ============================================================================ +// NAVIGATION UTILITY TYPES +// ============================================================================ + +/** + * NavigationHelper - Helper type for navigation functions + * + * Purpose: Provides type-safe navigation helper functions + */ +export type NavigationHelper = { + navigateToPatientDetails: ( + patientId: string, + patientName: string, + medicalCase: MedicalCase + ) => void; + navigateBack: () => void; +}; + +/* + * End of File: navigationTypes.ts + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ diff --git a/app/modules/PatientCare/redux/patientCareSlice.ts b/app/modules/PatientCare/redux/patientCareSlice.ts index 08c28cb..aacc24d 100644 --- a/app/modules/PatientCare/redux/patientCareSlice.ts +++ b/app/modules/PatientCare/redux/patientCareSlice.ts @@ -26,7 +26,6 @@ export const fetchPatients = createAsyncThunk( try { // Make actual API call to fetch medical cases const response :any = await patientAPI.getPatients(token); - console.log('patients response',response) if (response.ok && response.data&&response.data.success) { // Add random case types to each patient record const caseTypes: Array<'Critical' | 'Emergency' | 'Routine'> = ['Critical', 'Emergency', 'Routine']; @@ -36,7 +35,6 @@ export const fetchPatients = createAsyncThunk( type: caseTypes[Math.floor(Math.random() * caseTypes.length)] })); - console.log('patients with random types', patientsWithTypes); return patientsWithTypes as MedicalCase[]; } else { // Fallback to mock data for development @@ -125,7 +123,7 @@ export const fetchPatientDetails = createAsyncThunk( async (patientId: string, { rejectWithValue }) => { try { // TODO: Replace with actual API call - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve as any, 1000)); // Mock patient details for specific patient const mockPatient: MedicalCase = { @@ -179,7 +177,7 @@ export const updatePatient = createAsyncThunk( async (patientData: Partial & { id: number }, { rejectWithValue }) => { try { // TODO: Replace with actual API call - await new Promise((resolve) => setTimeout(resolve, 800)); + await new Promise((resolve) => setTimeout(resolve as any, 800)); return patientData; } catch (error) { return rejectWithValue('Failed to update patient.'); @@ -388,8 +386,8 @@ const patientCareSlice = createSlice({ state.isLoading = false; state.patients = action.payload; state.totalItems = action.payload.length; - state.lastUpdated = new Date(); - state.cacheExpiry = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes + state.lastUpdated = new Date().toLocaleDateString(); + state.cacheExpiry = new Date(Date.now() + 5 * 60 * 1000).toLocaleDateString(); // 5 minutes state.error = null; }) .addCase(fetchPatients.rejected, (state, action) => { diff --git a/app/modules/PatientCare/screens/PatientDetailsScreen.tsx b/app/modules/PatientCare/screens/PatientDetailsScreen.tsx new file mode 100644 index 0000000..d790898 --- /dev/null +++ b/app/modules/PatientCare/screens/PatientDetailsScreen.tsx @@ -0,0 +1,1042 @@ +/* + * File: PatientDetailsScreen.tsx + * Description: Comprehensive patient details screen with DICOM image viewer + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ + +import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import { + View, + Text, + StyleSheet, + ScrollView, + TouchableOpacity, + StatusBar, + Alert, + Dimensions, + Image, + FlatList, + RefreshControl, +} from 'react-native'; +import { theme } from '../../../theme/theme'; +import { useAppDispatch } from '../../../store/hooks'; +import Icon from 'react-native-vector-icons/Feather'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +// Import types +import { MedicalCase, PatientDetails, Series } from '../../../shared/types'; + +// Import components +import { ImageViewer } from '../components'; +import { API_CONFIG } from '../../../shared/utils'; + +// Get screen dimensions +const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); + +// ============================================================================ +// INTERFACES +// ============================================================================ + +interface PatientDetailsScreenProps { + navigation: any; + route: { + params: { + patientId: string; + patientName: string; + medicalCase: MedicalCase; + }; + }; +} + +interface ParsedPatientData { + patientDetails: PatientDetails; + series: Series[]; +} + +// ============================================================================ +// PATIENT DETAILS SCREEN COMPONENT +// ============================================================================ + +/** + * PatientDetailsScreen Component + * + * Purpose: Comprehensive patient details display with DICOM image viewer + * + * Features: + * - Full patient demographic information + * - Medical case details and status + * - DICOM series information + * - Image gallery with thumbnail previews + * - Real-time data updates + * - Emergency actions for critical cases + * - Medical history and notes + * - Modern healthcare-focused UI design + * - Responsive layout for different screen sizes + */ +const PatientDetailsScreen: React.FC = ({ navigation, route }) => { + // ============================================================================ + // STATE MANAGEMENT + // ============================================================================ + + const dispatch = useAppDispatch(); + + // Route parameters + const { patientId, patientName, medicalCase } = route.params; + + // Local state + const [isRefreshing, setIsRefreshing] = useState(false); + const [selectedImageIndex, setSelectedImageIndex] = useState(0); + const [showFullImage, setShowFullImage] = useState(false); + const [activeTab, setActiveTab] = useState<'overview' | 'images' | 'history'>('overview'); + + // ============================================================================ + // DATA PARSING & PROCESSING + // ============================================================================ + + /** + * Parse Patient Data + * + * Purpose: Safely parse JSON strings from medical case data + */ + const parsedData: ParsedPatientData = useMemo(() => { + 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 {}; + }; + + const patientDetails = parseJsonSafely(medicalCase.patientdetails); + const series = parseJsonSafely(patientDetails.series); + + return { + patientDetails: patientDetails.patientdetails || patientDetails, + series: Array.isArray(series) ? series : [], + }; + }, [medicalCase]); + + // ============================================================================ + // LIFECYCLE METHODS + // ============================================================================ + + /** + * Component Mount Effect + * + * Purpose: Initialize screen and set up navigation + */ + useEffect(() => { + // Set navigation title + navigation.setOptions({ + title: patientName || 'Patient Details', + headerShown: false, + }); + }, [navigation, patientName]); + + // ============================================================================ + // EVENT HANDLERS + // ============================================================================ + + /** + * Handle Refresh + * + * Purpose: Pull-to-refresh functionality + */ + const handleRefresh = useCallback(async () => { + setIsRefreshing(true); + // TODO: Implement refresh logic + setTimeout(() => { + setIsRefreshing(false); + }, 1000); + }, []); + + /** + * Handle Image Press + * + * Purpose: Open full-screen image viewer for selected series + * + * @param seriesIndex - Index of the series + */ + const handleImagePress = useCallback((seriesIndex: number) => { + setSelectedImageIndex(seriesIndex); + setShowFullImage(true); + }, []); + + /** + * Handle Close Image Viewer + * + * Purpose: Close full-screen image viewer + */ + const handleCloseImageViewer = useCallback(() => { + setShowFullImage(false); + }, []); + + /** + * Get All Images from Series + * + * Purpose: Extract image paths from DICOM series pngpath + */ + const getAllImages = useCallback(() => { + const images: string[] = []; + parsedData.series.forEach(series => { + // Use pngpath for actual image display + if (series.pngpath && typeof series.pngpath === 'string') { + images.push(series.pngpath); + } + }); + return images; + }, [parsedData.series]); + + /** + * Get Series Info for Image Index + * + * Purpose: Get series information for a given image index + */ + const getSeriesInfoForImage = useCallback((imageIndex: number) => { + if (imageIndex >= 0 && imageIndex < parsedData.series.length) { + const series = parsedData.series[imageIndex]; + return { + seriesNum: series.SeriesNum || '1', + seriesDesc: series.SerDes || 'Unnamed Series', + imageInSeries: 1, + totalInSeries: 1 + }; + } + return { + seriesNum: '1', + seriesDesc: 'Unknown Series', + imageInSeries: 1, + totalInSeries: 1 + }; + }, [parsedData.series]); + + /** + * Handle Emergency Action + * + * Purpose: Handle emergency actions for critical patients + */ + const handleEmergencyAction = useCallback(() => { + Alert.alert( + 'Emergency Action Required', + `Patient ${parsedData.patientDetails.Name} requires immediate attention`, + [ + { + text: 'Call Code Blue', + style: 'destructive', + onPress: () => { + // TODO: Implement emergency code calling + Alert.alert('Emergency', 'Code Blue activated'); + }, + }, + { + text: 'Alert Team', + onPress: () => { + // TODO: Implement team alerting + Alert.alert('Alert', 'Team notified'); + }, + }, + { + text: 'Cancel', + style: 'cancel', + }, + ] + ); + }, [parsedData.patientDetails.Name]); + + /** + * Handle Back Navigation + * + * Purpose: Navigate back to previous screen + */ + const handleBackPress = useCallback(() => { + navigation.goBack(); + }, [navigation]); + + // ============================================================================ + // RENDER HELPERS + // ============================================================================ + + /** + * Render Patient Header + * + * Purpose: Render patient identification and status section + */ + const renderPatientHeader = () => ( + + + + + + + + {parsedData.patientDetails.Name || 'Unknown Patient'} + + + MRN: {parsedData.patientDetails.PatID || 'N/A'} + + + + {parsedData.patientDetails.PatAge || 'N/A'} • {parsedData.patientDetails.PatSex || 'N/A'} + + + {medicalCase.type} + + + + + + {medicalCase.type === 'Critical' && ( + + + EMERGENCY + + )} + + ); + + /** + * Render Tab Navigation + * + * Purpose: Render tab navigation for different sections + */ + const renderTabNavigation = () => ( + + {[ + { key: 'overview', label: 'Overview', icon: 'info' }, + { key: 'images', label: 'Images', icon: 'image', count: parsedData.series.length }, + { key: 'history', label: 'History', icon: 'clock' }, + ].map((tab) => ( + setActiveTab(tab.key as any)} + > + + + {tab.label} + + {tab.count !== undefined && ( + + {tab.count} + + )} + + ))} + + ); + + /** + * Render Overview Tab + * + * Purpose: Render patient overview information + */ + const renderOverviewTab = () => ( + + {/* Medical Case Information */} + + Case Information + + + Case ID + {medicalCase.id} + + + Type + {medicalCase.type} + + + Created + + {new Date(medicalCase.created_at).toLocaleDateString()} + + + + Updated + + {new Date(medicalCase.updated_at).toLocaleDateString()} + + + + + + {/* Patient Details */} + + Patient Details + + + Name + {parsedData.patientDetails.Name || 'N/A'} + + + Age + {parsedData.patientDetails.PatAge || 'N/A'} + + + Sex + {parsedData.patientDetails.PatSex || 'N/A'} + + + Status + {parsedData.patientDetails.Status || 'N/A'} + + + Institution + {parsedData.patientDetails.InstName || 'N/A'} + + + Modality + {parsedData.patientDetails.Modality || 'N/A'} + + + + + {/* Series Information */} + {parsedData.series.length > 0 && ( + + Imaging Series + + + {parsedData.series.length} series available + + + Total series: {parsedData.series.length} + + + + )} + + ); + + /** + * Render Images Tab + * + * Purpose: Render DICOM image gallery + */ + const renderImagesTab = () => ( + + {parsedData.series.length === 0 ? ( + + + No Images Available + + No DICOM images are currently available for this patient + + + ) : ( + + DICOM Images + + {parsedData.series.map((series, seriesIndex) => ( + + + + Series {series.SeriesNum}: {series.SerDes || 'Unnamed Series'} + + + {series.ImgTotalinSeries || '0'} images in Path • {series.ViePos || 'Unknown position'} + + + + {/* Series Details */} + + + Series Number: + + {series.SeriesNum || 'N/A'} + + + + Total Images: + + {series.ImgTotalinSeries || '0'} + + + + Description: + + {series.SerDes || 'N/A'} + + + {series.Path && Array.isArray(series.Path) && ( + + Path Array: + + {series.Path.length} URLs available + + + )} + {series.ViePos && ( + + View Position: + + {series.ViePos} + + + )} + + + {/* Series Image */} + {series.pngpath ? ( + handleImagePress(seriesIndex)} + > + + + Series Image + + + ) : ( + + + No Image Available + + )} + + ))} + + )} + + ); + + /** + * Render History Tab + * + * Purpose: Render patient medical history + */ + const renderHistoryTab = () => ( + + + Medical History + + + + Case created on {new Date(medicalCase.created_at).toLocaleDateString()} + + + + + + Last updated on {new Date(medicalCase.updated_at).toLocaleDateString()} + + + + + + Status: {medicalCase.type} case + + + + + + Notes + + No additional notes available for this patient case. + + + + ); + + /** + * Get Status Color + * + * Purpose: Get appropriate color for patient status + * + * @param status - Patient status + */ + const getStatusColor = (status: string) => { + switch (status) { + case 'Critical': + return theme.colors.error; + case 'Emergency': + return theme.colors.warning; + case 'Routine': + return theme.colors.success; + default: + return theme.colors.info; + } + }; + + // ============================================================================ + // MAIN RENDER + // ============================================================================ + + return ( + + + + {/* Header */} + + + + + + Patient Details + Emergency Department + + + + + + + {/* Patient Header */} + {renderPatientHeader()} + + {/* Tab Navigation */} + {renderTabNavigation()} + + {/* Tab Content */} + + } + > + {activeTab === 'overview' && renderOverviewTab()} + {activeTab === 'images' && renderImagesTab()} + {activeTab === 'history' && renderHistoryTab()} + + + {/* Full-Screen Image Viewer */} + + + + + ); +}; + +// ============================================================================ +// 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, + fontWeight: 'bold', + 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, + }, + + // Patient Header Styles + patientHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: theme.spacing.md, + paddingVertical: theme.spacing.lg, + backgroundColor: theme.colors.background, + borderBottomWidth: 1, + borderBottomColor: theme.colors.border, + }, + patientInfo: { + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }, + patientAvatar: { + width: 60, + height: 60, + borderRadius: 30, + backgroundColor: theme.colors.backgroundAlt, + justifyContent: 'center', + alignItems: 'center', + marginRight: theme.spacing.md, + }, + patientDetails: { + flex: 1, + }, + patientName: { + fontSize: 20, + fontWeight: 'bold', + color: theme.colors.textPrimary, + fontFamily: theme.typography.fontFamily.bold, + marginBottom: 4, + }, + patientId: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.medium, + marginBottom: 8, + }, + patientMeta: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + patientMetaText: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.regular, + }, + statusBadge: { + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 16, + }, + statusText: { + fontSize: 12, + fontWeight: 'bold', + color: theme.colors.background, + textTransform: 'uppercase', + }, + emergencyButton: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: theme.colors.error, + paddingHorizontal: 16, + paddingVertical: 12, + borderRadius: 8, + shadowColor: theme.colors.error, + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.3, + shadowRadius: 4, + elevation: 4, + }, + emergencyButtonText: { + color: theme.colors.background, + fontSize: 12, + fontWeight: 'bold', + marginLeft: 8, + textTransform: 'uppercase', + }, + + // Tab Navigation Styles + tabContainer: { + flexDirection: 'row', + backgroundColor: theme.colors.background, + borderBottomWidth: 1, + borderBottomColor: theme.colors.border, + }, + tabButton: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: theme.spacing.md, + paddingHorizontal: theme.spacing.sm, + position: 'relative', + }, + activeTabButton: { + borderBottomWidth: 2, + borderBottomColor: theme.colors.primary, + }, + tabLabel: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.medium, + marginLeft: 8, + }, + activeTabLabel: { + color: theme.colors.primary, + fontFamily: theme.typography.fontFamily.bold, + }, + tabCount: { + backgroundColor: theme.colors.primary, + borderRadius: 10, + paddingHorizontal: 6, + paddingVertical: 2, + marginLeft: 8, + }, + tabCountText: { + color: theme.colors.background, + fontSize: 10, + fontWeight: 'bold', + }, + + // Content Styles + content: { + flex: 1, + }, + tabContent: { + padding: theme.spacing.md, + }, + + // Section Styles + section: { + marginBottom: theme.spacing.lg, + }, + sectionTitle: { + fontSize: 18, + fontWeight: 'bold', + color: theme.colors.textPrimary, + fontFamily: theme.typography.fontFamily.bold, + marginBottom: theme.spacing.md, + }, + + // Info Grid Styles + infoGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + }, + infoItem: { + width: '48%', + marginBottom: theme.spacing.md, + }, + infoLabel: { + fontSize: 12, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.medium, + marginBottom: 4, + textTransform: 'uppercase', + }, + infoValue: { + fontSize: 14, + color: theme.colors.textPrimary, + fontFamily: theme.typography.fontFamily.regular, + }, + + // Series Info Styles + seriesInfo: { + backgroundColor: theme.colors.backgroundAlt, + padding: theme.spacing.md, + borderRadius: 8, + }, + seriesText: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.regular, + marginBottom: 4, + }, + + // Images Styles + imagesContainer: { + marginTop: theme.spacing.sm, + }, + seriesContainer: { + marginBottom: theme.spacing.lg, + }, + seriesHeader: { + marginBottom: theme.spacing.md, + }, + seriesTitle: { + fontSize: 16, + fontWeight: 'bold', + color: theme.colors.textPrimary, + fontFamily: theme.typography.fontFamily.bold, + marginBottom: 4, + }, + seriesMeta: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.regular, + }, + imageList: { + paddingRight: theme.spacing.md, + }, + imageThumbnail: { + width: 120, + height: 120, + borderRadius: 8, + marginRight: theme.spacing.sm, + position: 'relative', + shadowColor: '#000000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 2, + }, + thumbnailImage: { + width: '100%', + height: '100%', + borderRadius: 8, + }, + imageOverlay: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + backgroundColor: 'rgba(0, 0, 0, 0.7)', + paddingVertical: 4, + paddingHorizontal: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + }, + imageNumber: { + color: theme.colors.background, + fontSize: 10, + fontWeight: 'bold', + textAlign: 'center', + }, + + // History Styles + historyItem: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: theme.spacing.sm, + }, + historyText: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.regular, + marginLeft: theme.spacing.sm, + }, + notesText: { + fontSize: 14, + color: theme.colors.textSecondary, + fontFamily: theme.typography.fontFamily.regular, + lineHeight: 20, + }, + + // Empty State Styles + emptyState: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: theme.spacing.xl, + }, + emptyStateTitle: { + fontSize: 18, + fontWeight: 'bold', + 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, + }, + + // No Image Placeholder Styles + noImagePlaceholder: { + width: 120, + height: 120, + borderRadius: 8, + marginRight: theme.spacing.sm, + backgroundColor: theme.colors.backgroundAlt, + justifyContent: 'center', + alignItems: 'center', + borderWidth: 1, + borderColor: theme.colors.border, + borderStyle: 'dashed', + }, + noImageText: { + fontSize: 10, + color: theme.colors.textMuted, + fontFamily: theme.typography.fontFamily.regular, + textAlign: 'center', + marginTop: 4, + }, + + // Series Details Styles + seriesDetails: { + backgroundColor: theme.colors.backgroundAlt, + padding: theme.spacing.sm, + borderRadius: 8, + marginBottom: theme.spacing.md, + }, + seriesDetailItem: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'flex-start', + marginBottom: theme.spacing.xs, + }, + 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', + }, +}); + +export default PatientDetailsScreen; + +/* + * End of File: PatientDetailsScreen.tsx + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ diff --git a/app/modules/PatientCare/screens/PatientsScreen.tsx b/app/modules/PatientCare/screens/PatientsScreen.tsx index 9500d28..ae0d21c 100644 --- a/app/modules/PatientCare/screens/PatientsScreen.tsx +++ b/app/modules/PatientCare/screens/PatientsScreen.tsx @@ -56,6 +56,7 @@ import LoadingState from '../components/LoadingState'; // Import types import { MedicalCase, PatientDetails, Series } from '../../../shared/types'; +import { PatientsScreenProps } from '../navigation/navigationTypes'; // Get screen dimensions const { width: screenWidth } = Dimensions.get('window'); @@ -64,10 +65,6 @@ const { width: screenWidth } = Dimensions.get('window'); // INTERFACES // ============================================================================ -interface PatientsScreenProps { - navigation: any; -} - // ============================================================================ // PATIENTS SCREEN COMPONENT // ============================================================================ @@ -241,7 +238,7 @@ const PatientsScreen: React.FC = ({ navigation }) => { const patientData = patientDetails.patientdetails || patientDetails; navigation.navigate('PatientDetails', { - patientId: patient.id, + patientId:'1', patientName: patientData.Name || 'Unknown Patient', medicalCase: patient, }); @@ -397,7 +394,7 @@ const PatientsScreen: React.FC = ({ navigation }) => { style={styles.headerButton} onPress={() => { // TODO: Implement notifications screen - navigation.navigate('Notifications'); + Alert.alert('Notifications', 'Notifications feature coming soon'); }} > diff --git a/app/modules/PatientCare/screens/index.ts b/app/modules/PatientCare/screens/index.ts index 582cd0c..704f549 100644 --- a/app/modules/PatientCare/screens/index.ts +++ b/app/modules/PatientCare/screens/index.ts @@ -6,6 +6,7 @@ */ export { default as PatientsScreen } from './PatientsScreen'; +export { default as PatientDetailsScreen } from './PatientDetailsScreen'; /* * End of File: index.ts diff --git a/app/modules/Settings/navigation/SettingsStackNavigator.tsx b/app/modules/Settings/navigation/SettingsStackNavigator.tsx index 88c79f3..ec940ac 100644 --- a/app/modules/Settings/navigation/SettingsStackNavigator.tsx +++ b/app/modules/Settings/navigation/SettingsStackNavigator.tsx @@ -34,7 +34,7 @@ const Stack = createStackNavigator(); const SettingsStackNavigator: React.FC = () => { return ( { > {/* Settings Screen - Main settings entry point */} = ({ const userProfilePhoto = useAppSelector(selectUserProfilePhoto); const notificationPreferences = useAppSelector(selectNotificationPreferences); const dashboardSettings = useAppSelector(selectDashboardSettings); - console.log('user details i got', user); + // ============================================================================ // SETTINGS SECTIONS GENERATION diff --git a/app/navigation/MainTabNavigator.tsx b/app/navigation/MainTabNavigator.tsx index 95e7737..f36db9f 100644 --- a/app/navigation/MainTabNavigator.tsx +++ b/app/navigation/MainTabNavigator.tsx @@ -14,7 +14,7 @@ import { AIPredictionStackNavigator } from '../modules/AIPrediction/navigation'; import { MainTabParamList } from './navigationTypes'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import { ComingSoonScreen } from '../shared/components'; -import { PatientsScreen } from '../modules/PatientCare'; +import { PatientCareStackNavigator } from '../modules/PatientCare/navigation'; // Create the bottom tab navigator const Tab = createBottomTabNavigator(); @@ -86,7 +86,7 @@ export const MainTabNavigator: React.FC = () => { {/* Patients Tab - Patient list and management */} UIAppFonts - Roboto-Black.ttf - Roboto-Bold.ttf - Roboto-ExtraBold.ttf - Roboto-ExtraLight.ttf - Roboto-Light.ttf - Roboto-Medium.ttf - Roboto-Regular.ttf - Roboto-SemiBold.ttf + WorkSans-Bold.ttf + WorkSans-ExtraBold.ttf + WorkSans-ExtraLight.ttf + WorkSans-Light.ttf + WorkSans-Medium.ttf + WorkSans-Regular.ttf + WorkSans-SemiBold.ttf + WorkSans-Thin.ttf NSCameraUsageDescription - This app needs access to the camera to take photos and videos. - + This app needs access to the camera to take photos and videos. diff --git a/ios/link-assets-manifest.json b/ios/link-assets-manifest.json index 441aa21..5d7736d 100644 --- a/ios/link-assets-manifest.json +++ b/ios/link-assets-manifest.json @@ -2,36 +2,36 @@ "migIndex": 1, "data": [ { - "path": "app/assets/fonts/Roboto-Black.ttf", - "sha1": "d1678489a8d5645f16486ec52d77b651ff0bf327" + "path": "app/assets/fonts/WorkSans-Bold.ttf", + "sha1": "ec84061651ead3c3c5cbb61c2d338aca0bacdc1e" }, { - "path": "app/assets/fonts/Roboto-Bold.ttf", - "sha1": "508c35dee818addce6cc6d1fb6e42f039da5a7cf" + "path": "app/assets/fonts/WorkSans-ExtraBold.ttf", + "sha1": "0b371d1dbfbdd15db880bbd129b239530c71accb" }, { - "path": "app/assets/fonts/Roboto-ExtraBold.ttf", - "sha1": "3dbfd71b6fbcfbd8e7ee8a8dd033dc5aaad63249" + "path": "app/assets/fonts/WorkSans-ExtraLight.ttf", + "sha1": "74596e55487e2961b6c43993698d658e2ceee77b" }, { - "path": "app/assets/fonts/Roboto-ExtraLight.ttf", - "sha1": "df556e64732e5c272349e13cb5f87591a1ae779b" + "path": "app/assets/fonts/WorkSans-Light.ttf", + "sha1": "293e11dae7e8b930bf5eea0b06ca979531f22189" }, { - "path": "app/assets/fonts/Roboto-Light.ttf", - "sha1": "318b44c0a32848f78bf11d4fbf3355d00647a796" + "path": "app/assets/fonts/WorkSans-Medium.ttf", + "sha1": "c281f8454dd193c2260e43ae2de171c5dd4086e4" }, { - "path": "app/assets/fonts/Roboto-Medium.ttf", - "sha1": "fa5192203f85ddb667579e1bdf26f12098bb873b" + "path": "app/assets/fonts/WorkSans-Regular.ttf", + "sha1": "5e0183b29b57c54595c62ac6bc223b21f1434226" }, { - "path": "app/assets/fonts/Roboto-Regular.ttf", - "sha1": "3bff51436aa7eb995d84cfc592cc63e1316bb400" + "path": "app/assets/fonts/WorkSans-SemiBold.ttf", + "sha1": "64b8fe156fafce221a0f66504255257053fc6062" }, { - "path": "app/assets/fonts/Roboto-SemiBold.ttf", - "sha1": "9ca139684fe902c8310dd82991648376ac9838db" + "path": "app/assets/fonts/WorkSans-Thin.ttf", + "sha1": "a62251331038fdd079c47bc413a350efbf702db8" } ] }