crearted UI and integrated Api for Physician suggestion

This commit is contained in:
yashwin-foxy 2025-08-11 19:23:19 +05:30
parent e8492cd442
commit 80a1688e19
8 changed files with 1126 additions and 8 deletions

View File

@ -12,7 +12,7 @@ import Icon from 'react-native-vector-icons/Feather';
import { theme } from '../../../theme';
// Import screens
import { AIPredictionsScreen } from '../screens';
import { AIPredictionsScreen, AIPredictionDetailScreen } from '../screens';
import { ComingSoonScreen } from '../../../shared/components';
// Import types
@ -152,20 +152,20 @@ const AIPredictionStackNavigator: React.FC = () => {
{/* AI Prediction Details Screen */}
<Stack.Screen
name="AIPredictionDetails"
component={ComingSoonScreen}
component={AIPredictionDetailScreen}
options={({ navigation, route }) => ({
title: 'Prediction Details',
title: 'Create Suggestion',
headerLeft: () => (
<HeaderBackButton onPress={() => navigation.goBack()} />
),
headerRight: () => (
<HeaderActionButton
iconName="share-2"
iconName="help-circle"
onPress={() => {
// Share prediction details
console.log('Share prediction:', route.params?.caseId);
// Show help for suggestion form
console.log('Show help for case:', route.params?.caseId);
}}
accessibilityLabel="Share prediction"
accessibilityLabel="Help"
/>
),
})}

View File

@ -19,7 +19,7 @@ import type { RouteProp } from '@react-navigation/native';
*
* Screens:
* - AIPredictionList: Main list of AI predictions
* - AIPredictionDetails: Detailed view of a specific prediction
* - AIPredictionDetails: Detailed view of a specific prediction with suggestion form
* - AIPredictionFilters: Advanced filtering options
* - AIPredictionStats: Detailed statistics view
*/

View File

@ -0,0 +1,987 @@
/*
* File: AIPredictionDetailScreen.tsx
* Description: AI Prediction detail screen with suggestion form for updating AI findings
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState, useCallback, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TextInput,
TouchableOpacity,
Alert,
SafeAreaView,
StatusBar,
KeyboardAvoidingView,
Platform,
} from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
import Icon from 'react-native-vector-icons/Feather';
import { theme } from '../../../theme';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import { CustomModal } from '../../../shared/components/CustomModal';
import Toast from 'react-native-toast-message';
import { aiPredictionAPI } from '../services/aiPredictionAPI';
import { showError } from '../../../shared/utils/toast';
// Import Redux selectors
import { selectCurrentCase, selectIsLoadingCaseDetails, selectError } from '../redux';
import { selectUser } from '../../Auth/redux/authSelectors';
// Import types
import type { AIPredictionDetailsScreenProps } from '../navigation/navigationTypes';
import type { AIPredictionCase } from '../types';
// ============================================================================
// ENUMS & TYPES
// ============================================================================
/**
* Suggestion Type Enum
*
* Purpose: Define available suggestion types as specified by user
*/
export enum SuggestionType {
TREATMENT = 'Treatment',
FOLLOW_UP = 'Follow Up',
DIAGNOSIS = 'Diagnosis',
OTHER = 'Other'
}
/**
* Priority Enum
*
* Purpose: Define priority levels as specified by user
*/
export enum Priority {
LOW = 'Low',
MEDIUM = 'Medium',
HIGH = 'High',
CRITICAL = 'Critical'
}
/**
* Related Finding Interface
*
* Purpose: Structure for key-value pairs in related findings
*/
interface RelatedFinding {
key: string;
value: string;
}
/**
* Suggestion Form Data Interface
*
* Purpose: Structure for suggestion form data
*/
interface SuggestionFormData {
patientId: string;
suggestionType: SuggestionType;
title: string;
suggestionText: string;
confidence: string;
priority: Priority;
category: string;
costEstimate: string;
timeEstimate: string;
expiresAt: Date | null;
aiModelVersion: string;
evidenceSources: string;
contraindications: string;
tags: string;
relatedFindings: RelatedFinding[];
}
/**
* API Response Interface
*
* Purpose: Structure for API response data
*/
interface APIResponse {
success: boolean;
message: string;
data: {
created_at: string;
updated_at: string;
status: string;
suggestion_id: string;
};
}
// ============================================================================
// CONSTANTS
// ============================================================================
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 },
];
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 },
];
// ============================================================================
// AI PREDICTION DETAIL SCREEN COMPONENT
// ============================================================================
/**
* AIPredictionDetailScreen Component
*
* Purpose: Display AI prediction details and allow suggestion updates
*
* Features:
* - View complete AI prediction details
* - Create/update suggestions for AI findings
* - Form validation and submission
* - Dynamic related findings management
* - Date picker for expiration
* - Dropdown selections for types and priorities
* - Responsive design with keyboard handling
*/
const AIPredictionDetailScreen: React.FC<AIPredictionDetailsScreenProps> = ({
navigation,
route
}) => {
// ============================================================================
// REDUX STATE
// ============================================================================
const dispatch = useAppDispatch();
const user = useAppSelector(selectUser);
const currentCase = useAppSelector(selectCurrentCase);
const isLoading = useAppSelector(selectIsLoadingCaseDetails);
const error = useAppSelector(selectError);
// ============================================================================
// LOCAL STATE
// ============================================================================
const [formData, setFormData] = useState<SuggestionFormData>({
patientId: route.params.caseId,
suggestionType: SuggestionType.TREATMENT,
title: '',
suggestionText: '',
confidence: '0.99',
priority: Priority.HIGH,
category: 'Radiology',
costEstimate: '',
timeEstimate: '1-2 hours',
expiresAt: null,
aiModelVersion: 'v2.1.0',
evidenceSources: '',
contraindications: '',
tags: '',
relatedFindings: [
{ key: 'finding_label', value: 'midline shift' },
{ key: 'finding_type', value: 'no_pathology' },
{ key: 'clinical_urgency', value: 'urgent' },
{ key: 'primary_severity', value: 'high' },
{ key: 'modality', value: '/DX' },
{ key: 'institution', value: 'Brighton Radiology' },
],
});
const [showSuggestionTypeDropdown, setShowSuggestionTypeDropdown] = useState(false);
const [showPriorityDropdown, setShowPriorityDropdown] = useState(false);
const [showDatePicker, setShowDatePicker] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [apiResponse, setApiResponse] = useState<any>(null);
// ============================================================================
// EFFECTS
// ============================================================================
/**
* Initialize form data from current case
*/
useEffect(() => {
if (currentCase) {
setFormData(prev => ({
...prev,
patientId: currentCase.patid,
title: `Recommended CT Scan`,
suggestionText: `Describe your suggestion with clinical reasoning...`,
confidence: currentCase.prediction.confidence_score.toFixed(4),
}));
}
}, [currentCase]);
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* Handle form field changes
*/
const handleFieldChange = useCallback((field: keyof SuggestionFormData, value: any) => {
setFormData(prev => ({
...prev,
[field]: value,
}));
}, []);
/**
* Handle dropdown selection
*/
const handleDropdownSelect = useCallback((field: keyof SuggestionFormData, value: any) => {
setFormData(prev => ({
...prev,
[field]: value,
}));
setShowSuggestionTypeDropdown(false);
setShowPriorityDropdown(false);
}, []);
/**
* Handle date selection
*/
const handleDateChange = useCallback((event: any, selectedDate?: Date) => {
setShowDatePicker(false);
if (selectedDate) {
handleFieldChange('expiresAt', selectedDate);
}
}, [handleFieldChange]);
/**
* Add new related finding
*/
const handleAddRelatedFinding = useCallback(() => {
setFormData(prev => ({
...prev,
relatedFindings: [...prev.relatedFindings, { key: '', value: '' }],
}));
}, []);
/**
* Remove related finding
*/
const handleRemoveRelatedFinding = useCallback((index: number) => {
setFormData(prev => ({
...prev,
relatedFindings: prev.relatedFindings.filter((_, i) => i !== index),
}));
}, []);
/**
* Update related finding
*/
const handleUpdateRelatedFinding = useCallback((index: number, field: 'key' | 'value', value: string) => {
setFormData(prev => ({
...prev,
relatedFindings: prev.relatedFindings.map((finding, i) =>
i === index ? { ...finding, [field]: value } : finding
),
}));
}, []);
/**
* Validate form data
*/
const validateForm = useCallback((): boolean => {
if (!formData.title.trim()) {
showError('Validation Error', 'Title is required');
return false;
}
if (!formData.suggestionText.trim()) {
showError('Validation Error', 'Suggestion text is required');
return false;
}
if (!formData.patientId.trim()) {
showError('Validation Error', 'Patient ID is required');
return false;
}
// Validate expiry date - must be in the future
if (formData.expiresAt) {
const now = new Date();
const expiryDate = new Date(formData.expiresAt);
if (expiryDate <= now) {
showError('Validation Error', 'Expiry date must be in the future');
return false;
}
}
return true;
}, [formData]);
/**
* Handle form submission
*/
const handleSubmitSuggestion = useCallback(async () => {
if (!validateForm()) return;
setIsSubmitting(true);
try {
// Prepare API payload according to backend structure
const apiPayload = {
patid: formData.patientId,
suggestion_type: formData.suggestionType.toLowerCase().replace(' ', '_'),
suggestion_title: formData.title,
suggestion_text: formData.suggestionText,
confidence_score: parseFloat(formData.confidence),
priority_level: formData.priority.toLowerCase(),
category: formData.category,
related_findings: formData.relatedFindings.reduce((acc, finding) => {
if (finding.key && finding.value) {
acc[finding.key] = finding.value;
}
return acc;
}, {} as Record<string, string>),
evidence_sources: formData.evidenceSources ? formData.evidenceSources.split(',').map(s => s.trim()) : [],
contraindications: formData.contraindications || 'none',
cost_estimate: formData.costEstimate ? parseFloat(formData.costEstimate) : 0,
time_estimate: formData.timeEstimate,
expires_at: formData.expiresAt ? formData.expiresAt.toISOString() : null,
tags: formData.tags ? formData.tags.split(',').map(s => s.trim()) : [],
ai_model_version: formData.aiModelVersion,
};
// Call centralized API service
const response = await aiPredictionAPI.submitAISuggestion(apiPayload, user?.access_token || '');
console.log('feed back response',response)
if (response.ok && response.data) {
const responseData = response.data as APIResponse;
if (responseData.success) {
// Show success toast
Toast.show({
type: 'success',
text1: 'Success!',
text2: responseData.message || 'Suggestion submitted successfully',
position: 'top',
visibilityTime: 4000,
});
// Store API response data
setApiResponse(responseData.data);
// Show success modal with API response data
setShowSuccessModal(true);
} else {
throw new Error(responseData.message || 'Failed to submit suggestion');
}
} else {
throw new Error(response.problem || 'HTTP request failed');
}
} catch (error) {
console.error('Submission Error:', error);
// Show error toast
Toast.show({
type: 'error',
text1: 'Error',
text2: error instanceof Error ? error.message : 'Failed to submit suggestion. Please try again.',
position: 'top',
visibilityTime: 5000,
});
} finally {
setIsSubmitting(false);
}
}, [formData, validateForm, user]);
/**
* Handle success modal close
*/
const handleSuccessModalClose = useCallback(() => {
setShowSuccessModal(false);
navigation.goBack();
}, [navigation]);
/**
* Handle back navigation
*/
const handleGoBack = useCallback(() => {
navigation.goBack();
}, [navigation]);
// ============================================================================
// RENDER FUNCTIONS
// ============================================================================
/**
* Render dropdown options
*/
const renderDropdownOptions = useCallback((
options: Array<{ label: string; value: any; color?: string }>,
currentValue: any,
onSelect: (value: any) => void
) => (
<View style={styles.dropdownOptions}>
{options.map((option) => (
<TouchableOpacity
key={option.value}
style={[
styles.dropdownOption,
currentValue === option.value && styles.dropdownOptionSelected
]}
onPress={() => onSelect(option.value)}
>
<Text style={[
styles.dropdownOptionText,
currentValue === option.value && styles.dropdownOptionTextSelected,
option.color && { color: option.color }
]}>
{option.label}
</Text>
{currentValue === option.value && (
<Icon name="check" size={16} color={theme.colors.primary} />
)}
</TouchableOpacity>
))}
</View>
), []);
/**
* Render related findings section
*/
const renderRelatedFindings = useCallback(() => (
<View style={styles.relatedFindingsContainer}>
<Text style={styles.sectionTitle}>Related Findings</Text>
{formData.relatedFindings.map((finding, index) => (
<View key={index} style={styles.relatedFindingItem}>
<TextInput
style={[styles.input, styles.relatedFindingKey]}
value={finding.key}
onChangeText={(value) => handleUpdateRelatedFinding(index, 'key', value)}
placeholder="Key"
placeholderTextColor={theme.colors.textMuted}
/>
<TextInput
style={[styles.input, styles.relatedFindingValue]}
value={finding.value}
onChangeText={(value) => handleUpdateRelatedFinding(index, 'value', value)}
placeholder="Value"
placeholderTextColor={theme.colors.textMuted}
/>
<TouchableOpacity
style={styles.removeButton}
onPress={() => handleRemoveRelatedFinding(index)}
accessibilityRole="button"
accessibilityLabel="Remove related finding"
>
<Icon name="trash-2" size={18} color={theme.colors.error} />
</TouchableOpacity>
</View>
))}
<TouchableOpacity
style={styles.addButton}
onPress={handleAddRelatedFinding}
>
<Text style={styles.addButtonText}>Add</Text>
</TouchableOpacity>
</View>
), [formData.relatedFindings, handleUpdateRelatedFinding, handleRemoveRelatedFinding, handleAddRelatedFinding]);
// ============================================================================
// RENDER
// ============================================================================
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor={theme.colors.background} />
{/* Header */}
<KeyboardAvoidingView
style={styles.keyboardAvoidingView}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
enabled={Platform.OS === 'ios'}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
>
{/* Patient ID */}
<View style={styles.formGroup}>
<Text style={styles.label}>Patient ID</Text>
<TextInput
style={[styles.input, styles.disabledInput]}
value={formData.patientId}
editable={false}
placeholder="Patient ID"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
{/* Suggestion Type */}
<View style={styles.formGroup}>
<Text style={styles.label}>Suggestion Type</Text>
<View style={styles.dropdownContainer}>
<TouchableOpacity
style={styles.dropdown}
onPress={() => setShowSuggestionTypeDropdown(!showSuggestionTypeDropdown)}
>
<Text style={styles.dropdownText}>{formData.suggestionType}</Text>
<Icon name="chevron-down" size={20} color={theme.colors.textSecondary} />
</TouchableOpacity>
{showSuggestionTypeDropdown && renderDropdownOptions(
SUGGESTION_TYPE_OPTIONS,
formData.suggestionType,
(value) => handleDropdownSelect('suggestionType', value)
)}
</View>
</View>
{/* Title */}
<View style={styles.formGroup}>
<Text style={styles.label}>Title</Text>
<TextInput
style={styles.input}
value={formData.title}
onChangeText={(value) => handleFieldChange('title', value)}
placeholder="Recommended CT Scan"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
{/* Suggestion Text */}
<View style={styles.formGroup}>
<Text style={styles.label}>Suggestion Text</Text>
<TextInput
style={[styles.input, styles.textArea]}
value={formData.suggestionText}
onChangeText={(value) => handleFieldChange('suggestionText', value)}
placeholder="Describe your suggestion with clinical reasoning..."
placeholderTextColor={theme.colors.textMuted}
multiline
numberOfLines={4}
textAlignVertical="top"
/>
</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}
value={formData.confidence}
onChangeText={(value) => handleFieldChange('confidence', value)}
placeholder="0.9979"
placeholderTextColor={theme.colors.textMuted}
keyboardType="numeric"
/>
</View>
<View style={[styles.formGroup, styles.formGroupHalf]}>
<Text style={styles.label}>Priority</Text>
<View style={styles.dropdownContainer}>
<TouchableOpacity
style={styles.dropdown}
onPress={() => setShowPriorityDropdown(!showPriorityDropdown)}
>
<Text style={styles.dropdownText}>{formData.priority}</Text>
<Icon name="chevron-down" size={20} color={theme.colors.textSecondary} />
</TouchableOpacity>
{showPriorityDropdown && renderDropdownOptions(
PRIORITY_OPTIONS,
formData.priority,
(value) => handleDropdownSelect('priority', value)
)}
</View>
</View>
</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}
value={formData.category}
onChangeText={(value) => handleFieldChange('category', value)}
placeholder="Radiology"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
<View style={[styles.formGroup, styles.formGroupHalf]}>
<Text style={styles.label}>Cost Estimate (USD)</Text>
<TextInput
style={styles.input}
value={formData.costEstimate}
onChangeText={(value) => handleFieldChange('costEstimate', value)}
placeholder="Enter cost"
placeholderTextColor={theme.colors.textMuted}
keyboardType="numeric"
/>
</View>
</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}
value={formData.timeEstimate}
onChangeText={(value) => handleFieldChange('timeEstimate', value)}
placeholder="1-2 hours"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
<View style={[styles.formGroup, styles.formGroupHalf]}>
<Text style={styles.label}>Expires At</Text>
<TouchableOpacity
style={styles.dateInput}
onPress={() => setShowDatePicker(true)}
>
<Text style={[
styles.dateText,
!formData.expiresAt && styles.placeholderText
]}>
{formData.expiresAt
? formData.expiresAt.toLocaleDateString()
: '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>
{/* AI Model Version */}
<View style={styles.formGroup}>
<Text style={styles.label}>AI Model Version</Text>
<TextInput
style={styles.input}
value={formData.aiModelVersion}
onChangeText={(value) => handleFieldChange('aiModelVersion', value)}
placeholder="v2.1.0"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
{/* Evidence Sources */}
<View style={styles.formGroup}>
<Text style={styles.label}>Evidence Sources (comma-separated)</Text>
<TextInput
style={styles.input}
value={formData.evidenceSources}
onChangeText={(value) => handleFieldChange('evidenceSources', value)}
placeholder="Evidence A, Protocol B"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
{/* Contraindications */}
<View style={styles.formGroup}>
<Text style={styles.label}>Contraindications</Text>
<TextInput
style={[styles.input, styles.textArea]}
value={formData.contraindications}
onChangeText={(value) => handleFieldChange('contraindications', value)}
placeholder="Any known contraindications..."
placeholderTextColor={theme.colors.textMuted}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
{/* Tags */}
<View style={styles.formGroup}>
<Text style={styles.label}>Tags (comma-separated)</Text>
<TextInput
style={styles.input}
value={formData.tags}
onChangeText={(value) => handleFieldChange('tags', value)}
placeholder="emergency, chest, pulmonary"
placeholderTextColor={theme.colors.textMuted}
/>
</View>
{/* Related Findings */}
{renderRelatedFindings()}
{/* Submit Button */}
<TouchableOpacity
style={[styles.submitButton, isSubmitting && styles.submitButtonDisabled]}
onPress={handleSubmitSuggestion}
disabled={isSubmitting}
>
{isSubmitting ? (
<Text style={styles.submitButtonText}>Submitting...</Text>
) : (
<Text style={styles.submitButtonText}>Submit Suggestion</Text>
)}
</TouchableOpacity>
</ScrollView>
</KeyboardAvoidingView>
{/* Date Picker */}
{showDatePicker && (
<DateTimePicker
value={formData.expiresAt || new Date()}
mode="date"
display="default"
onChange={handleDateChange}
minimumDate={new Date()}
/>
)}
{/* Success Modal */}
<CustomModal
visible={showSuccessModal}
title="Suggestion Submitted Successfully!"
message={
apiResponse ?
`Your suggestion has been submitted and will be reviewed by the medical team.\n\n` +
`Suggestion ID: ${apiResponse.suggestion_id}\n` +
`Status: ${apiResponse.status}\n` +
`Created: ${new Date(apiResponse.created_at).toLocaleString()}`
:
"Your suggestion has been successfully submitted and will be reviewed by the medical team."
}
type="success"
confirmText="OK"
onConfirm={handleSuccessModalClose}
onClose={handleSuccessModalClose}
/>
{/* Toast Messages */}
<Toast />
</SafeAreaView>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md,
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
backgroundColor: theme.colors.background,
},
backButton: {
padding: theme.spacing.sm,
},
headerTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
headerSpacer: {
width: 40,
},
keyboardAvoidingView: {
flex: 1,
// Ensure this doesn't interfere with bottom tab navigation
position: 'relative',
},
scrollView: {
flex: 1,
},
scrollContent: {
padding: theme.spacing.md,
paddingBottom: theme.spacing.xl,
},
formGroup: {
marginBottom: theme.spacing.md,
},
formGroupHalf: {
flex: 1,
},
formRow: {
flexDirection: 'row',
gap: theme.spacing.md,
},
label: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
input: {
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
backgroundColor: theme.colors.background,
},
disabledInput: {
backgroundColor: theme.colors.backgroundAlt,
color: theme.colors.textSecondary,
borderColor: theme.colors.border,
},
textArea: {
height: 80,
textAlignVertical: 'top',
},
dropdownContainer: {
position: 'relative',
},
dropdown: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
backgroundColor: theme.colors.background,
},
dropdownText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
dropdownOptions: {
position: 'absolute',
top: '100%',
left: 0,
right: 0,
zIndex: 1000,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.background,
marginTop: theme.spacing.xs,
...theme.shadows.medium,
},
dropdownOption: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
dropdownOptionSelected: {
backgroundColor: theme.colors.tertiary,
},
dropdownOptionText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
dropdownOptionTextSelected: {
color: theme.colors.primary,
fontFamily: theme.typography.fontFamily.medium,
},
dateInput: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
backgroundColor: theme.colors.background,
},
dateText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
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,
},
relatedFindingsContainer: {
marginBottom: theme.spacing.lg,
},
relatedFindingItem: {
flexDirection: 'row',
alignItems: 'center',
gap: theme.spacing.sm,
marginBottom: theme.spacing.sm,
},
relatedFindingKey: {
flex: 1,
},
relatedFindingValue: {
flex: 1,
},
addButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
alignItems: 'center',
alignSelf: 'flex-start',
},
addButtonText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.background,
},
removeButton: {
padding: theme.spacing.xs,
justifyContent: 'center',
alignItems: 'center',
},
submitButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
alignItems: 'center',
marginTop: theme.spacing.lg,
...theme.shadows.medium,
},
submitButtonDisabled: {
backgroundColor: theme.colors.textMuted,
},
submitButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
});
export default AIPredictionDetailScreen;
/*
* End of File: AIPredictionDetailScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -6,6 +6,7 @@
*/
export { default as AIPredictionsScreen } from './AIPredictionsScreen';
export { default as AIPredictionDetailScreen } from './AIPredictionDetailScreen';
/*
* End of File: index.ts

View File

@ -223,6 +223,110 @@ export const aiPredictionAPI = {
improvement_suggestions?: string;
}, token: string) => {
return api.post(`/api/ai-cases/feedback/${caseId}`, feedbackData, buildHeaders({ token }));
},
/**
* Submit AI Suggestion
*
* Purpose: Submit physician suggestions for AI findings
*
* @param suggestionData - Suggestion data including patient ID, type, title, text, etc.
* @param token - Authentication token
* @returns Promise with suggestion submission result
*/
submitAISuggestion: (suggestionData: {
patid: string;
suggestion_type: string;
suggestion_title: string;
suggestion_text: string;
confidence_score: number;
priority_level: string;
category: string;
related_findings: Record<string, string>;
evidence_sources: string[];
contraindications: string;
cost_estimate: number;
time_estimate: string;
expires_at: string | null;
tags: string[];
ai_model_version: string;
}, token: string) => {
return api.post('/api/ai-cases/suggestions', suggestionData, buildHeaders({ token }));
},
/**
* Get AI Suggestions
*
* Purpose: Fetch AI suggestions for a specific case or all suggestions
*
* @param token - Authentication token
* @param params - Optional query parameters
* @returns Promise with suggestions data
*/
getAISuggestions: (token: string, params?: {
caseId?: string;
patientId?: string;
suggestionType?: string;
priority?: string;
status?: string;
page?: number;
limit?: number;
}) => {
const queryParams = params ? { ...params } : {};
return api.get('/api/ai-cases/suggestions', queryParams, buildHeaders({ token }));
},
/**
* Update AI Suggestion
*
* Purpose: Update an existing AI suggestion
*
* @param suggestionId - Suggestion ID to update
* @param updateData - Updated suggestion data
* @param token - Authentication token
* @returns Promise with updated suggestion data
*/
updateAISuggestion: (suggestionId: string, updateData: {
suggestion_title?: string;
suggestion_text?: string;
priority_level?: string;
category?: string;
related_findings?: Record<string, string>;
evidence_sources?: string[];
contraindications?: string;
cost_estimate?: number;
time_estimate?: string;
expires_at?: string | null;
tags?: string[];
}, token: string) => {
return api.put(`/api/ai-cases/suggestions/${suggestionId}`, updateData, buildHeaders({ token }));
},
/**
* Delete AI Suggestion
*
* Purpose: Delete an AI suggestion
*
* @param suggestionId - Suggestion ID to delete
* @param token - Authentication token
* @returns Promise with deletion result
*/
deleteAISuggestion: (suggestionId: string, token: string) => {
return api.delete(`/api/ai-cases/suggestions/${suggestionId}`, {}, buildHeaders({ token }));
},
/**
* Get Suggestion Statistics
*
* Purpose: Fetch statistics about AI suggestions
*
* @param token - Authentication token
* @param timeRange - Optional time range filter
* @returns Promise with suggestion statistics
*/
getSuggestionStats: (token: string, timeRange?: 'today' | 'week' | 'month') => {
const params = timeRange ? { timeRange } : {};
return api.get('/api/ai-cases/suggestions/statistics', params, buildHeaders({ token }));
}
};

View File

@ -44,6 +44,7 @@ export const MainTabNavigator: React.FC = () => {
// Tab bar styling for active and inactive states
tabBarActiveTintColor: theme.colors.primary, // Blue color for active tab
tabBarInactiveTintColor: theme.colors.textMuted, // Gray color for inactive tab
tabBarHideOnKeyboard: true,
// Tab bar container styling
tabBarStyle: {

24
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"@react-native-async-storage/async-storage": "^2.1.0",
"@react-native-clipboard/clipboard": "^1.16.1",
"@react-native-community/datetimepicker": "^8.4.4",
"@react-native-community/netinfo": "^11.4.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.0.14",
@ -3207,6 +3208,29 @@
"node": ">=10"
}
},
"node_modules/@react-native-community/datetimepicker": {
"version": "8.4.4",
"resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.4.tgz",
"integrity": "sha512-bc4ZixEHxZC9/qf5gbdYvIJiLZ5CLmEsC3j+Yhe1D1KC/3QhaIfGDVdUcid0PdlSoGOSEq4VlB93AWyetEyBSQ==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4"
},
"peerDependencies": {
"expo": ">=52.0.0",
"react": "*",
"react-native": "*",
"react-native-windows": "*"
},
"peerDependenciesMeta": {
"expo": {
"optional": true
},
"react-native-windows": {
"optional": true
}
}
},
"node_modules/@react-native-community/netinfo": {
"version": "11.4.1",
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.4.1.tgz",

View File

@ -12,6 +12,7 @@
"dependencies": {
"@react-native-async-storage/async-storage": "^2.1.0",
"@react-native-clipboard/clipboard": "^1.16.1",
"@react-native-community/datetimepicker": "^8.4.4",
"@react-native-community/netinfo": "^11.4.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.0.14",