added patient detail screen
This commit is contained in:
parent
80a1688e19
commit
413a1d74de
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-Bold.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-Bold.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-ExtraBold.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-ExtraLight.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-Light.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-Light.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-Medium.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-Medium.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-Regular.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-Regular.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-SemiBold.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/WorkSans-Thin.ttf
Normal file
BIN
android/app/src/main/assets/fonts/WorkSans-Thin.ttf
Normal file
Binary file not shown.
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app/assets/fonts/WorkSans-Bold.ttf
Normal file
BIN
app/assets/fonts/WorkSans-Bold.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-ExtraBold.ttf
Normal file
BIN
app/assets/fonts/WorkSans-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-ExtraLight.ttf
Normal file
BIN
app/assets/fonts/WorkSans-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-Light.ttf
Normal file
BIN
app/assets/fonts/WorkSans-Light.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-Medium.ttf
Normal file
BIN
app/assets/fonts/WorkSans-Medium.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-Regular.ttf
Normal file
BIN
app/assets/fonts/WorkSans-Regular.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-SemiBold.ttf
Normal file
BIN
app/assets/fonts/WorkSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/WorkSans-Thin.ttf
Normal file
BIN
app/assets/fonts/WorkSans-Thin.ttf
Normal file
Binary file not shown.
@ -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<AIPredictionDetailsScreenProps> = ({
|
||||
navigation,
|
||||
@ -201,6 +205,7 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
const [showSuccessModal, setShowSuccessModal] = useState(false);
|
||||
const [apiResponse, setApiResponse] = useState<any>(null);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// EFFECTS
|
||||
// ============================================================================
|
||||
@ -246,6 +251,14 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
setShowPriorityDropdown(false);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Close all dropdowns
|
||||
*/
|
||||
const closeAllDropdowns = useCallback(() => {
|
||||
setShowSuggestionTypeDropdown(false);
|
||||
setShowPriorityDropdown(false);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Handle date selection
|
||||
*/
|
||||
@ -408,15 +421,33 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// RENDER FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Render dropdown options
|
||||
* Render enhanced header
|
||||
*/
|
||||
const renderHeader = () => (
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity style={styles.backButton} onPress={handleGoBack}>
|
||||
<Icon name="arrow-left" size={24} color={theme.colors.primary} />
|
||||
</TouchableOpacity>
|
||||
<View style={styles.headerContent}>
|
||||
<Text style={styles.headerTitle}>AI Prediction Details</Text>
|
||||
<Text style={styles.headerSubtitle}>Create Medical Suggestion</Text>
|
||||
</View>
|
||||
<View style={styles.headerSpacer} />
|
||||
</View>
|
||||
);
|
||||
|
||||
/**
|
||||
* 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,6 +461,15 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
]}
|
||||
onPress={() => onSelect(option.value)}
|
||||
>
|
||||
<View style={styles.dropdownOptionContent}>
|
||||
{option.icon && (
|
||||
<Icon
|
||||
name={option.icon as any}
|
||||
size={16}
|
||||
color={option.color || theme.colors.textSecondary}
|
||||
style={styles.dropdownOptionIcon}
|
||||
/>
|
||||
)}
|
||||
<Text style={[
|
||||
styles.dropdownOptionText,
|
||||
currentValue === option.value && styles.dropdownOptionTextSelected,
|
||||
@ -437,6 +477,7 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
]}>
|
||||
{option.label}
|
||||
</Text>
|
||||
</View>
|
||||
{currentValue === option.value && (
|
||||
<Icon name="check" size={16} color={theme.colors.primary} />
|
||||
)}
|
||||
@ -446,27 +487,33 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
), []);
|
||||
|
||||
/**
|
||||
* Render related findings section
|
||||
* Render enhanced related findings section
|
||||
*/
|
||||
const renderRelatedFindings = useCallback(() => (
|
||||
<View style={styles.relatedFindingsContainer}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Icon name="link" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.sectionTitle}>Related Findings</Text>
|
||||
</View>
|
||||
<View style={styles.relatedFindingsContent}>
|
||||
{formData.relatedFindings.map((finding, index) => (
|
||||
<View key={index} style={styles.relatedFindingItem}>
|
||||
<View style={styles.relatedFindingInputs}>
|
||||
<TextInput
|
||||
style={[styles.input, styles.relatedFindingKey]}
|
||||
value={finding.key}
|
||||
onChangeText={(value) => handleUpdateRelatedFinding(index, 'key', value)}
|
||||
placeholder="Key"
|
||||
placeholder="Finding Key"
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.input, styles.relatedFindingValue]}
|
||||
value={finding.value}
|
||||
onChangeText={(value) => handleUpdateRelatedFinding(index, 'value', value)}
|
||||
placeholder="Value"
|
||||
placeholder="Finding Value"
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
/>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.removeButton}
|
||||
onPress={() => handleRemoveRelatedFinding(index)}
|
||||
@ -481,9 +528,11 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
style={styles.addButton}
|
||||
onPress={handleAddRelatedFinding}
|
||||
>
|
||||
<Text style={styles.addButtonText}>Add</Text>
|
||||
<Icon name="plus" size={20} color={theme.colors.background} />
|
||||
<Text style={styles.addButtonText}>Add Finding</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
), [formData.relatedFindings, handleUpdateRelatedFinding, handleRemoveRelatedFinding, handleAddRelatedFinding]);
|
||||
|
||||
// ============================================================================
|
||||
@ -494,8 +543,12 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="dark-content" backgroundColor={theme.colors.background} />
|
||||
|
||||
{/* Header */}
|
||||
<TouchableWithoutFeedback onPress={closeAllDropdowns}>
|
||||
<>
|
||||
{/* Enhanced Header */}
|
||||
{renderHeader()}
|
||||
|
||||
<View style={styles.mainContent}>
|
||||
<KeyboardAvoidingView
|
||||
style={styles.keyboardAvoidingView}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
@ -508,7 +561,12 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{/* Patient ID */}
|
||||
{/* Patient ID Card */}
|
||||
<View style={styles.patientCard}>
|
||||
<View style={styles.patientCardHeader}>
|
||||
<Icon name="user" size={24} color={theme.colors.primary} />
|
||||
<Text style={styles.patientCardTitle}>Patient Information</Text>
|
||||
</View>
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Patient ID</Text>
|
||||
<TextInput
|
||||
@ -519,16 +577,29 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Suggestion Type */}
|
||||
{/* Suggestion Type Card */}
|
||||
<View style={[styles.formCard, {zIndex:1}]}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="edit-3" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>Suggestion Type</Text>
|
||||
</View>
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Suggestion Type</Text>
|
||||
<Text style={styles.label}>Type</Text>
|
||||
<View style={styles.dropdownContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.dropdown}
|
||||
style={styles.enhancedDropdown}
|
||||
onPress={() => setShowSuggestionTypeDropdown(!showSuggestionTypeDropdown)}
|
||||
>
|
||||
<View style={styles.dropdownContent}>
|
||||
<Icon
|
||||
name={SUGGESTION_TYPE_OPTIONS.find(opt => 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}
|
||||
/>
|
||||
<Text style={styles.dropdownText}>{formData.suggestionType}</Text>
|
||||
</View>
|
||||
<Icon name="chevron-down" size={20} color={theme.colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
{showSuggestionTypeDropdown && renderDropdownOptions(
|
||||
@ -538,12 +609,20 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Main Suggestion Card */}
|
||||
<View style={styles.formCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="file-text" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>Suggestion Details</Text>
|
||||
</View>
|
||||
|
||||
{/* Title */}
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Title</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.title}
|
||||
onChangeText={(value) => handleFieldChange('title', value)}
|
||||
placeholder="Recommended CT Scan"
|
||||
@ -555,7 +634,7 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Suggestion Text</Text>
|
||||
<TextInput
|
||||
style={[styles.input, styles.textArea]}
|
||||
style={[styles.enhancedInput, styles.textArea]}
|
||||
value={formData.suggestionText}
|
||||
onChangeText={(value) => handleFieldChange('suggestionText', value)}
|
||||
placeholder="Describe your suggestion with clinical reasoning..."
|
||||
@ -565,13 +644,21 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
textAlignVertical="top"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Confidence & Priority Card */}
|
||||
<View style={[styles.formCard, {zIndex:2}]}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="bar-chart-2" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>Assessment & Priority</Text>
|
||||
</View>
|
||||
|
||||
{/* Row 1: Confidence and Priority */}
|
||||
<View style={styles.formRow}>
|
||||
<View style={[styles.formGroup, styles.formGroupHalf]}>
|
||||
<Text style={styles.label}>Confidence (0-1)</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.confidence}
|
||||
onChangeText={(value) => handleFieldChange('confidence', value)}
|
||||
placeholder="0.9979"
|
||||
@ -584,10 +671,16 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<Text style={styles.label}>Priority</Text>
|
||||
<View style={styles.dropdownContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.dropdown}
|
||||
style={styles.enhancedDropdown}
|
||||
onPress={() => setShowPriorityDropdown(!showPriorityDropdown)}
|
||||
>
|
||||
<View style={styles.dropdownContent}>
|
||||
<View style={[
|
||||
styles.priorityIndicator,
|
||||
{ backgroundColor: PRIORITY_OPTIONS.find(opt => opt.value === formData.priority)?.bgColor || theme.colors.backgroundAlt }
|
||||
]} />
|
||||
<Text style={styles.dropdownText}>{formData.priority}</Text>
|
||||
</View>
|
||||
<Icon name="chevron-down" size={20} color={theme.colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
{showPriorityDropdown && renderDropdownOptions(
|
||||
@ -598,13 +691,21 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Category & Cost Card */}
|
||||
<View style={styles.formCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="tag" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>Classification & Resources</Text>
|
||||
</View>
|
||||
|
||||
{/* Row 2: Category and Cost Estimate */}
|
||||
<View style={styles.formRow}>
|
||||
<View style={[styles.formGroup, styles.formGroupHalf]}>
|
||||
<Text style={styles.label}>Category</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.category}
|
||||
onChangeText={(value) => handleFieldChange('category', value)}
|
||||
placeholder="Radiology"
|
||||
@ -615,7 +716,7 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<View style={[styles.formGroup, styles.formGroupHalf]}>
|
||||
<Text style={styles.label}>Cost Estimate (USD)</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.costEstimate}
|
||||
onChangeText={(value) => handleFieldChange('costEstimate', value)}
|
||||
placeholder="Enter cost"
|
||||
@ -624,13 +725,21 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Time & Expiry Card */}
|
||||
<View style={styles.formCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="clock" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>Timeline</Text>
|
||||
</View>
|
||||
|
||||
{/* Row 3: Time Estimate and Expires At */}
|
||||
<View style={styles.formRow}>
|
||||
<View style={[styles.formGroup, styles.formGroupHalf]}>
|
||||
<Text style={styles.label}>Time Estimate</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.timeEstimate}
|
||||
onChangeText={(value) => handleFieldChange('timeEstimate', value)}
|
||||
placeholder="1-2 hours"
|
||||
@ -641,9 +750,10 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<View style={[styles.formGroup, styles.formGroupHalf]}>
|
||||
<Text style={styles.label}>Expires At</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.dateInput}
|
||||
style={styles.enhancedDateInput}
|
||||
onPress={() => setShowDatePicker(true)}
|
||||
>
|
||||
<Icon name="calendar" size={18} color={theme.colors.primary} />
|
||||
<Text style={[
|
||||
styles.dateText,
|
||||
!formData.expiresAt && styles.placeholderText
|
||||
@ -653,17 +763,24 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
: 'Select date'
|
||||
}
|
||||
</Text>
|
||||
<Icon name="calendar" size={20} color={theme.colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.helperText}>Date must be in the future</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* AI Model & Evidence Card */}
|
||||
<View style={styles.formCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="cpu" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>AI Model & Evidence</Text>
|
||||
</View>
|
||||
|
||||
{/* AI Model Version */}
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>AI Model Version</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.aiModelVersion}
|
||||
onChangeText={(value) => handleFieldChange('aiModelVersion', value)}
|
||||
placeholder="v2.1.0"
|
||||
@ -675,19 +792,27 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Evidence Sources (comma-separated)</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.evidenceSources}
|
||||
onChangeText={(value) => handleFieldChange('evidenceSources', value)}
|
||||
placeholder="Evidence A, Protocol B"
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Contraindications & Tags Card */}
|
||||
<View style={styles.formCard}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Icon name="alert-triangle" size={20} color={theme.colors.primary} />
|
||||
<Text style={styles.cardTitle}>Safety & Organization</Text>
|
||||
</View>
|
||||
|
||||
{/* Contraindications */}
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Contraindications</Text>
|
||||
<TextInput
|
||||
style={[styles.input, styles.textArea]}
|
||||
style={[styles.enhancedInput, styles.textArea]}
|
||||
value={formData.contraindications}
|
||||
onChangeText={(value) => handleFieldChange('contraindications', value)}
|
||||
placeholder="Any known contraindications..."
|
||||
@ -702,13 +827,14 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={styles.label}>Tags (comma-separated)</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
style={styles.enhancedInput}
|
||||
value={formData.tags}
|
||||
onChangeText={(value) => handleFieldChange('tags', value)}
|
||||
placeholder="emergency, chest, pulmonary"
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Related Findings */}
|
||||
{renderRelatedFindings()}
|
||||
@ -720,13 +846,23 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<View style={styles.submitButtonContent}>
|
||||
<Icon name="loader" size={20} color={theme.colors.background} style={styles.spinningIcon} />
|
||||
<Text style={styles.submitButtonText}>Submitting...</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.submitButtonContent}>
|
||||
<Icon name="send" size={20} color={theme.colors.background} />
|
||||
<Text style={styles.submitButtonText}>Submit Suggestion</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</>
|
||||
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
{/* Date Picker */}
|
||||
{showDatePicker && (
|
||||
@ -771,32 +907,48 @@ const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
|
||||
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',
|
||||
},
|
||||
scrollView: {
|
||||
@ -806,6 +958,62 @@ const styles = StyleSheet.create({
|
||||
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,10 +1174,28 @@ 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,
|
||||
@ -921,14 +1207,34 @@ const styles = StyleSheet.create({
|
||||
marginTop: theme.spacing.xs,
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
|
||||
// Section Styles
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing.md,
|
||||
},
|
||||
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;
|
||||
|
||||
@ -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
|
||||
|
||||
503
app/modules/PatientCare/components/ImageViewer.tsx
Normal file
503
app/modules/PatientCare/components/ImageViewer.tsx
Normal file
@ -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<ImageViewerProps> = ({
|
||||
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 = () => (
|
||||
<View style={styles.header}>
|
||||
<View style={styles.headerLeft}>
|
||||
<TouchableOpacity
|
||||
style={styles.closeButton}
|
||||
onPress={handleClose}
|
||||
>
|
||||
<Icon name="x" size={24} color={theme.colors.background} />
|
||||
</TouchableOpacity>
|
||||
<View style={styles.patientInfo}>
|
||||
<Text style={styles.patientName}>{patientName}</Text>
|
||||
<Text style={styles.seriesInfo}>{seriesInfo}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.headerRight}>
|
||||
<Text style={styles.imageCounter}>
|
||||
{currentIndex + 1} of {images.length}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
/**
|
||||
* Render Navigation Controls
|
||||
*
|
||||
* Purpose: Render image navigation controls
|
||||
*/
|
||||
const renderNavigationControls = () => (
|
||||
<View style={styles.navigationControls}>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.navButton,
|
||||
currentIndex === 0 && styles.navButtonDisabled
|
||||
]}
|
||||
onPress={handlePrevious}
|
||||
disabled={currentIndex === 0}
|
||||
>
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
size={24}
|
||||
color={currentIndex === 0 ? theme.colors.textMuted : theme.colors.background}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.navButton,
|
||||
currentIndex === images.length - 1 && styles.navButtonDisabled
|
||||
]}
|
||||
onPress={handleNext}
|
||||
disabled={currentIndex === images.length - 1}
|
||||
>
|
||||
<Icon
|
||||
name="chevron-right"
|
||||
size={24}
|
||||
color={currentIndex === images.length - 1 ? theme.colors.textMuted : theme.colors.background}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
/**
|
||||
* Render Zoom Controls
|
||||
*
|
||||
* Purpose: Render zoom control buttons
|
||||
*/
|
||||
const renderZoomControls = () => (
|
||||
<View style={styles.zoomControls}>
|
||||
<TouchableOpacity
|
||||
style={styles.zoomButton}
|
||||
onPress={handleZoomOut}
|
||||
>
|
||||
<Icon name="minus" size={20} color={theme.colors.background} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.zoomButton}
|
||||
onPress={handleResetZoom}
|
||||
>
|
||||
<Text style={styles.zoomText}>{Math.round(scale * 100)}%</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.zoomButton}
|
||||
onPress={handleZoomIn}
|
||||
>
|
||||
<Icon name="plus" size={20} color={theme.colors.background} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// MAIN RENDER
|
||||
// ============================================================================
|
||||
|
||||
if (!visible || images.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="light-content" backgroundColor="#000000" />
|
||||
|
||||
{/* Header */}
|
||||
{renderHeader()}
|
||||
|
||||
{/* Main Image Area */}
|
||||
<View style={styles.imageContainer}>
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
maximumZoomScale={3}
|
||||
minimumZoomScale={0.5}
|
||||
bounces={false}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: API_CONFIG.DICOM_BASE_URL + images[currentIndex] }}
|
||||
style={[
|
||||
styles.image,
|
||||
{
|
||||
transform: [{ scale }],
|
||||
},
|
||||
]}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
{renderNavigationControls()}
|
||||
|
||||
{/* Zoom Controls */}
|
||||
{renderZoomControls()}
|
||||
|
||||
{/* Thumbnail Strip */}
|
||||
<View style={styles.thumbnailStrip}>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.thumbnailContent}
|
||||
>
|
||||
{images.map((image, index) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={[
|
||||
styles.thumbnail,
|
||||
index === currentIndex && styles.activeThumbnail
|
||||
]}
|
||||
onPress={() => setCurrentIndex(index)}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: API_CONFIG.DICOM_BASE_URL + image }}
|
||||
style={styles.thumbnailImage}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
{index === currentIndex && (
|
||||
<View style={styles.activeIndicator}>
|
||||
<Icon name="check" size={12} color={theme.colors.background} />
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 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.
|
||||
*/
|
||||
@ -158,7 +158,7 @@ const PatientCard: React.FC<PatientCardProps> = ({
|
||||
|
||||
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<PatientCardProps> = ({
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.container,
|
||||
patient.type === 'Critical' && styles.containerCritical,
|
||||
// patient.type === 'Critical' && styles.containerCritical,
|
||||
{ borderLeftColor: typeConfig.color }
|
||||
]}
|
||||
onPress={onPress}
|
||||
|
||||
@ -10,6 +10,7 @@ export { default as SearchBar } from './SearchBar';
|
||||
export { default as FilterTabs } from './FilterTabs';
|
||||
export { default as EmptyState } from './EmptyState';
|
||||
export { default as LoadingState } from './LoadingState';
|
||||
export { default as ImageViewer } from './ImageViewer';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* File: PatientCareStackNavigator.tsx
|
||||
* Description: Stack navigator for PatientCare module navigation
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
|
||||
// Import screens
|
||||
import { PatientsScreen, PatientDetailsScreen } from '../screens';
|
||||
|
||||
// Import types
|
||||
import { PatientCareStackParamList } from './navigationTypes';
|
||||
|
||||
// ============================================================================
|
||||
// STACK NAVIGATOR
|
||||
// ============================================================================
|
||||
|
||||
const Stack = createStackNavigator<PatientCareStackParamList>();
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Stack.Navigator
|
||||
initialRouteName="PatientsScreen"
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
cardStyle: { backgroundColor: 'transparent' },
|
||||
cardOverlayEnabled: false,
|
||||
gestureEnabled: true,
|
||||
gestureDirection: 'horizontal',
|
||||
}}
|
||||
>
|
||||
{/* Patients Screen - Main patient list */}
|
||||
<Stack.Screen
|
||||
name="PatientsScreen"
|
||||
component={PatientsScreen}
|
||||
options={{
|
||||
title: 'Patients',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Patient Details Screen - Comprehensive patient information */}
|
||||
<Stack.Screen
|
||||
name="PatientDetails"
|
||||
component={PatientDetailsScreen}
|
||||
options={{
|
||||
title: 'Patient Details',
|
||||
gestureEnabled: true,
|
||||
gestureDirection: 'horizontal',
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
export default PatientCareStackNavigator;
|
||||
|
||||
/*
|
||||
* End of File: PatientCareStackNavigator.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
15
app/modules/PatientCare/navigation/index.ts
Normal file
15
app/modules/PatientCare/navigation/index.ts
Normal file
@ -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.
|
||||
*/
|
||||
118
app/modules/PatientCare/navigation/navigationTypes.ts
Normal file
118
app/modules/PatientCare/navigation/navigationTypes.ts
Normal file
@ -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<PatientCareStackParamList>;
|
||||
|
||||
// ============================================================================
|
||||
// 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.
|
||||
*/
|
||||
@ -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<MedicalCase> & { 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) => {
|
||||
|
||||
1042
app/modules/PatientCare/screens/PatientDetailsScreen.tsx
Normal file
1042
app/modules/PatientCare/screens/PatientDetailsScreen.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -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<PatientsScreenProps> = ({ 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<PatientsScreenProps> = ({ navigation }) => {
|
||||
style={styles.headerButton}
|
||||
onPress={() => {
|
||||
// TODO: Implement notifications screen
|
||||
navigation.navigate('Notifications');
|
||||
Alert.alert('Notifications', 'Notifications feature coming soon');
|
||||
}}
|
||||
>
|
||||
<Icon name="bell" size={20} color={theme.colors.textSecondary} />
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
export { default as PatientsScreen } from './PatientsScreen';
|
||||
export { default as PatientDetailsScreen } from './PatientDetailsScreen';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
|
||||
@ -34,7 +34,7 @@ const Stack = createStackNavigator<SettingsStackParamList>();
|
||||
const SettingsStackNavigator: React.FC = () => {
|
||||
return (
|
||||
<Stack.Navigator
|
||||
initialRouteName="Settings"
|
||||
initialRouteName="SettingScreen"
|
||||
screenOptions={{
|
||||
// Header styling for settings screens
|
||||
headerStyle: {
|
||||
@ -71,7 +71,7 @@ const SettingsStackNavigator: React.FC = () => {
|
||||
>
|
||||
{/* Settings Screen - Main settings entry point */}
|
||||
<Stack.Screen
|
||||
name="Settings"
|
||||
name="SettingScreen"
|
||||
component={SettingsScreen}
|
||||
options={{
|
||||
title: 'Settings',
|
||||
|
||||
@ -16,7 +16,7 @@ import { UserProfile, UserPreferences } from '../../../shared/types';
|
||||
*/
|
||||
export type SettingsStackParamList = {
|
||||
// Settings screen - Main settings with profile and preferences
|
||||
Settings: SettingsScreenParams;
|
||||
SettingScreen: SettingsScreenParams;
|
||||
|
||||
// Profile Edit screen - Edit user profile information
|
||||
ProfileEdit: ProfileEditScreenParams;
|
||||
|
||||
@ -108,7 +108,7 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
||||
const userProfilePhoto = useAppSelector(selectUserProfilePhoto);
|
||||
const notificationPreferences = useAppSelector(selectNotificationPreferences);
|
||||
const dashboardSettings = useAppSelector(selectDashboardSettings);
|
||||
console.log('user details i got', user);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// SETTINGS SECTIONS GENERATION
|
||||
|
||||
@ -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<MainTabParamList>();
|
||||
@ -86,7 +86,7 @@ export const MainTabNavigator: React.FC = () => {
|
||||
{/* Patients Tab - Patient list and management */}
|
||||
<Tab.Screen
|
||||
name="Patients"
|
||||
component={PatientsScreen} // TODO: Replace with actual PatientsScreen
|
||||
component={PatientCareStackNavigator}
|
||||
options={{
|
||||
title: 'Patient List',
|
||||
tabBarLabel: 'Patients',
|
||||
|
||||
@ -150,8 +150,8 @@ export interface PatientCareState {
|
||||
totalItems: number;
|
||||
|
||||
// Cache
|
||||
lastUpdated: Date | null;
|
||||
cacheExpiry: Date | null;
|
||||
lastUpdated: string | null;
|
||||
cacheExpiry: string | null;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -9,6 +9,7 @@ import Config from 'react-native-config';
|
||||
// API Configuration
|
||||
export const API_CONFIG = {
|
||||
BASE_URL:Config.BASE_URL,
|
||||
DICOM_BASE_URL:'https://demo.medpacsystems.com',
|
||||
TIMEOUT: 30000,
|
||||
RETRY_ATTEMPTS: 3,
|
||||
RETRY_DELAY: 1000,
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
export const typography = {
|
||||
// Font Families
|
||||
fontFamily: {
|
||||
bold: 'Roboto-Bold',
|
||||
medium: 'Roboto-Medium',
|
||||
regular: 'Roboto-Regular',
|
||||
light: 'Roboto-Light',
|
||||
semibold: 'Roboto-SemiBold',
|
||||
extrabold: 'Roboto-ExtraBold',
|
||||
bold: 'WorkSans-Bold',
|
||||
medium: 'WorkSans-Medium',
|
||||
regular: 'WorkSans-Regular',
|
||||
light: 'WorkSans-Light',
|
||||
semibold: 'WorkSans-SemiBold',
|
||||
extrabold: 'WorkSans-ExtraBold',
|
||||
},
|
||||
|
||||
// Font Weights
|
||||
|
||||
@ -11,14 +11,14 @@
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
9BFF7E8F967043379EA34EF5 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C93B020BF2D44311BFAF317D /* Roboto-Black.ttf */; };
|
||||
64ADC498A16B4DD2979F9EC6 /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E8DE31AC3A3B46AFBAB8E623 /* Roboto-Bold.ttf */; };
|
||||
9A05BCBBB57F42CF891B4E76 /* Roboto-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6D158A6D27784CB3AAFC4572 /* Roboto-ExtraBold.ttf */; };
|
||||
E9AB985237534D1A92E3A5C8 /* Roboto-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6787284EF58F40B4A29BE810 /* Roboto-ExtraLight.ttf */; };
|
||||
A345282A09764F4B8935E1CE /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F3B7B24AB39048E28AA013C6 /* Roboto-Light.ttf */; };
|
||||
6C3A045CF24641D79616809F /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 57615910A7564380B1D23731 /* Roboto-Medium.ttf */; };
|
||||
A79B0AFEC3DA42AAAA56DC75 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B9D41D27862846A8A6563185 /* Roboto-Regular.ttf */; };
|
||||
264B0BD7896E430EB2761212 /* Roboto-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7539BBD08F0743178F5517A3 /* Roboto-SemiBold.ttf */; };
|
||||
90EC1A731F2F480594C0F9D1 /* WorkSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6606F41B1382422DA695F61C /* WorkSans-Bold.ttf */; };
|
||||
7E1975A765074059A27FA6F1 /* WorkSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1569DC7537534ED39A79EE9E /* WorkSans-ExtraBold.ttf */; };
|
||||
E09CC0AFCD63425FABA5714F /* WorkSans-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FF48E43749B74116A2C4083C /* WorkSans-ExtraLight.ttf */; };
|
||||
6A06861DDC314E49B482B4EB /* WorkSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8C4C96FCA144E859DC2AFDA /* WorkSans-Light.ttf */; };
|
||||
3BEFE3BEC3334662878A37D5 /* WorkSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 55E647ABF16B47F6A83403C0 /* WorkSans-Medium.ttf */; };
|
||||
6E9607F0F9DE4649802D618B /* WorkSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 589149FBA9F64E9F94AA50AF /* WorkSans-Regular.ttf */; };
|
||||
DCDB35B731E1422DBD1481D1 /* WorkSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A58733FB00504503BFBF4295 /* WorkSans-SemiBold.ttf */; };
|
||||
25B52D1C5AB64F039BEF062F /* WorkSans-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C3614D19A4004E3E858E4CEC /* WorkSans-Thin.ttf */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -43,14 +43,14 @@
|
||||
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = NeoScan_Physician/AppDelegate.swift; sourceTree = "<group>"; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = NeoScan_Physician/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
C93B020BF2D44311BFAF317D /* Roboto-Black.ttf */ = {isa = PBXFileReference; name = "Roboto-Black.ttf"; path = "../app/assets/fonts/Roboto-Black.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
E8DE31AC3A3B46AFBAB8E623 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; name = "Roboto-Bold.ttf"; path = "../app/assets/fonts/Roboto-Bold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
6D158A6D27784CB3AAFC4572 /* Roboto-ExtraBold.ttf */ = {isa = PBXFileReference; name = "Roboto-ExtraBold.ttf"; path = "../app/assets/fonts/Roboto-ExtraBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
6787284EF58F40B4A29BE810 /* Roboto-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Roboto-ExtraLight.ttf"; path = "../app/assets/fonts/Roboto-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
F3B7B24AB39048E28AA013C6 /* Roboto-Light.ttf */ = {isa = PBXFileReference; name = "Roboto-Light.ttf"; path = "../app/assets/fonts/Roboto-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
57615910A7564380B1D23731 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; name = "Roboto-Medium.ttf"; path = "../app/assets/fonts/Roboto-Medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
B9D41D27862846A8A6563185 /* Roboto-Regular.ttf */ = {isa = PBXFileReference; name = "Roboto-Regular.ttf"; path = "../app/assets/fonts/Roboto-Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
7539BBD08F0743178F5517A3 /* Roboto-SemiBold.ttf */ = {isa = PBXFileReference; name = "Roboto-SemiBold.ttf"; path = "../app/assets/fonts/Roboto-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
6606F41B1382422DA695F61C /* WorkSans-Bold.ttf */ = {isa = PBXFileReference; name = "WorkSans-Bold.ttf"; path = "../app/assets/fonts/WorkSans-Bold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
1569DC7537534ED39A79EE9E /* WorkSans-ExtraBold.ttf */ = {isa = PBXFileReference; name = "WorkSans-ExtraBold.ttf"; path = "../app/assets/fonts/WorkSans-ExtraBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
FF48E43749B74116A2C4083C /* WorkSans-ExtraLight.ttf */ = {isa = PBXFileReference; name = "WorkSans-ExtraLight.ttf"; path = "../app/assets/fonts/WorkSans-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
B8C4C96FCA144E859DC2AFDA /* WorkSans-Light.ttf */ = {isa = PBXFileReference; name = "WorkSans-Light.ttf"; path = "../app/assets/fonts/WorkSans-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
55E647ABF16B47F6A83403C0 /* WorkSans-Medium.ttf */ = {isa = PBXFileReference; name = "WorkSans-Medium.ttf"; path = "../app/assets/fonts/WorkSans-Medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
589149FBA9F64E9F94AA50AF /* WorkSans-Regular.ttf */ = {isa = PBXFileReference; name = "WorkSans-Regular.ttf"; path = "../app/assets/fonts/WorkSans-Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
A58733FB00504503BFBF4295 /* WorkSans-SemiBold.ttf */ = {isa = PBXFileReference; name = "WorkSans-SemiBold.ttf"; path = "../app/assets/fonts/WorkSans-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
C3614D19A4004E3E858E4CEC /* WorkSans-Thin.ttf */ = {isa = PBXFileReference; name = "WorkSans-Thin.ttf"; path = "../app/assets/fonts/WorkSans-Thin.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -136,14 +136,14 @@
|
||||
C38CA987921A4CA4AEDBB3E5 /* Resources */ = {
|
||||
isa = "PBXGroup";
|
||||
children = (
|
||||
C93B020BF2D44311BFAF317D /* Roboto-Black.ttf */,
|
||||
E8DE31AC3A3B46AFBAB8E623 /* Roboto-Bold.ttf */,
|
||||
6D158A6D27784CB3AAFC4572 /* Roboto-ExtraBold.ttf */,
|
||||
6787284EF58F40B4A29BE810 /* Roboto-ExtraLight.ttf */,
|
||||
F3B7B24AB39048E28AA013C6 /* Roboto-Light.ttf */,
|
||||
57615910A7564380B1D23731 /* Roboto-Medium.ttf */,
|
||||
B9D41D27862846A8A6563185 /* Roboto-Regular.ttf */,
|
||||
7539BBD08F0743178F5517A3 /* Roboto-SemiBold.ttf */,
|
||||
6606F41B1382422DA695F61C /* WorkSans-Bold.ttf */,
|
||||
1569DC7537534ED39A79EE9E /* WorkSans-ExtraBold.ttf */,
|
||||
FF48E43749B74116A2C4083C /* WorkSans-ExtraLight.ttf */,
|
||||
B8C4C96FCA144E859DC2AFDA /* WorkSans-Light.ttf */,
|
||||
55E647ABF16B47F6A83403C0 /* WorkSans-Medium.ttf */,
|
||||
589149FBA9F64E9F94AA50AF /* WorkSans-Regular.ttf */,
|
||||
A58733FB00504503BFBF4295 /* WorkSans-SemiBold.ttf */,
|
||||
C3614D19A4004E3E858E4CEC /* WorkSans-Thin.ttf */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
@ -218,14 +218,14 @@
|
||||
files = (
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
9BFF7E8F967043379EA34EF5 /* Roboto-Black.ttf in Resources */,
|
||||
64ADC498A16B4DD2979F9EC6 /* Roboto-Bold.ttf in Resources */,
|
||||
9A05BCBBB57F42CF891B4E76 /* Roboto-ExtraBold.ttf in Resources */,
|
||||
E9AB985237534D1A92E3A5C8 /* Roboto-ExtraLight.ttf in Resources */,
|
||||
A345282A09764F4B8935E1CE /* Roboto-Light.ttf in Resources */,
|
||||
6C3A045CF24641D79616809F /* Roboto-Medium.ttf in Resources */,
|
||||
A79B0AFEC3DA42AAAA56DC75 /* Roboto-Regular.ttf in Resources */,
|
||||
264B0BD7896E430EB2761212 /* Roboto-SemiBold.ttf in Resources */,
|
||||
90EC1A731F2F480594C0F9D1 /* WorkSans-Bold.ttf in Resources */,
|
||||
7E1975A765074059A27FA6F1 /* WorkSans-ExtraBold.ttf in Resources */,
|
||||
E09CC0AFCD63425FABA5714F /* WorkSans-ExtraLight.ttf in Resources */,
|
||||
6A06861DDC314E49B482B4EB /* WorkSans-Light.ttf in Resources */,
|
||||
3BEFE3BEC3334662878A37D5 /* WorkSans-Medium.ttf in Resources */,
|
||||
6E9607F0F9DE4649802D618B /* WorkSans-Regular.ttf in Resources */,
|
||||
DCDB35B731E1422DBD1481D1 /* WorkSans-SemiBold.ttf in Resources */,
|
||||
25B52D1C5AB64F039BEF062F /* WorkSans-Thin.ttf in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@ -49,17 +49,16 @@
|
||||
<false/>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Roboto-Black.ttf</string>
|
||||
<string>Roboto-Bold.ttf</string>
|
||||
<string>Roboto-ExtraBold.ttf</string>
|
||||
<string>Roboto-ExtraLight.ttf</string>
|
||||
<string>Roboto-Light.ttf</string>
|
||||
<string>Roboto-Medium.ttf</string>
|
||||
<string>Roboto-Regular.ttf</string>
|
||||
<string>Roboto-SemiBold.ttf</string>
|
||||
<string>WorkSans-Bold.ttf</string>
|
||||
<string>WorkSans-ExtraBold.ttf</string>
|
||||
<string>WorkSans-ExtraLight.ttf</string>
|
||||
<string>WorkSans-Light.ttf</string>
|
||||
<string>WorkSans-Medium.ttf</string>
|
||||
<string>WorkSans-Regular.ttf</string>
|
||||
<string>WorkSans-SemiBold.ttf</string>
|
||||
<string>WorkSans-Thin.ttf</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app needs access to the camera to take photos and videos.</string>
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user