diff --git a/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx b/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx index 20c1bbe..ede10d5 100644 --- a/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx +++ b/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx @@ -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 */} ({ - title: 'Prediction Details', + title: 'Create Suggestion', headerLeft: () => ( navigation.goBack()} /> ), headerRight: () => ( { - // 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" /> ), })} diff --git a/app/modules/AIPrediction/navigation/navigationTypes.ts b/app/modules/AIPrediction/navigation/navigationTypes.ts index e590439..0f8cf6a 100644 --- a/app/modules/AIPrediction/navigation/navigationTypes.ts +++ b/app/modules/AIPrediction/navigation/navigationTypes.ts @@ -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 */ diff --git a/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx b/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx new file mode 100644 index 0000000..87474af --- /dev/null +++ b/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx @@ -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 = ({ + 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({ + 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(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), + 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 + ) => ( + + {options.map((option) => ( + onSelect(option.value)} + > + + {option.label} + + {currentValue === option.value && ( + + )} + + ))} + + ), []); + + /** + * Render related findings section + */ + const renderRelatedFindings = useCallback(() => ( + + Related Findings + {formData.relatedFindings.map((finding, index) => ( + + handleUpdateRelatedFinding(index, 'key', value)} + placeholder="Key" + placeholderTextColor={theme.colors.textMuted} + /> + handleUpdateRelatedFinding(index, 'value', value)} + placeholder="Value" + placeholderTextColor={theme.colors.textMuted} + /> + handleRemoveRelatedFinding(index)} + accessibilityRole="button" + accessibilityLabel="Remove related finding" + > + + + + ))} + + Add + + + ), [formData.relatedFindings, handleUpdateRelatedFinding, handleRemoveRelatedFinding, handleAddRelatedFinding]); + + // ============================================================================ + // RENDER + // ============================================================================ + + return ( + + + + {/* Header */} + + + + {/* Patient ID */} + + Patient ID + + + + {/* Suggestion Type */} + + Suggestion Type + + setShowSuggestionTypeDropdown(!showSuggestionTypeDropdown)} + > + {formData.suggestionType} + + + {showSuggestionTypeDropdown && renderDropdownOptions( + SUGGESTION_TYPE_OPTIONS, + formData.suggestionType, + (value) => handleDropdownSelect('suggestionType', value) + )} + + + + {/* Title */} + + Title + handleFieldChange('title', value)} + placeholder="Recommended CT Scan" + placeholderTextColor={theme.colors.textMuted} + /> + + + {/* Suggestion Text */} + + Suggestion Text + handleFieldChange('suggestionText', value)} + placeholder="Describe your suggestion with clinical reasoning..." + placeholderTextColor={theme.colors.textMuted} + multiline + numberOfLines={4} + textAlignVertical="top" + /> + + + {/* Row 1: Confidence and Priority */} + + + Confidence (0-1) + handleFieldChange('confidence', value)} + placeholder="0.9979" + placeholderTextColor={theme.colors.textMuted} + keyboardType="numeric" + /> + + + + Priority + + setShowPriorityDropdown(!showPriorityDropdown)} + > + {formData.priority} + + + {showPriorityDropdown && renderDropdownOptions( + PRIORITY_OPTIONS, + formData.priority, + (value) => handleDropdownSelect('priority', value) + )} + + + + + {/* Row 2: Category and Cost Estimate */} + + + Category + handleFieldChange('category', value)} + placeholder="Radiology" + placeholderTextColor={theme.colors.textMuted} + /> + + + + Cost Estimate (USD) + handleFieldChange('costEstimate', value)} + placeholder="Enter cost" + placeholderTextColor={theme.colors.textMuted} + keyboardType="numeric" + /> + + + + {/* Row 3: Time Estimate and Expires At */} + + + Time Estimate + handleFieldChange('timeEstimate', value)} + placeholder="1-2 hours" + placeholderTextColor={theme.colors.textMuted} + /> + + + + Expires At + setShowDatePicker(true)} + > + + {formData.expiresAt + ? formData.expiresAt.toLocaleDateString() + : 'Select date' + } + + + + Date must be in the future + + + + {/* AI Model Version */} + + AI Model Version + handleFieldChange('aiModelVersion', value)} + placeholder="v2.1.0" + placeholderTextColor={theme.colors.textMuted} + /> + + + {/* Evidence Sources */} + + Evidence Sources (comma-separated) + handleFieldChange('evidenceSources', value)} + placeholder="Evidence A, Protocol B" + placeholderTextColor={theme.colors.textMuted} + /> + + + {/* Contraindications */} + + Contraindications + handleFieldChange('contraindications', value)} + placeholder="Any known contraindications..." + placeholderTextColor={theme.colors.textMuted} + multiline + numberOfLines={3} + textAlignVertical="top" + /> + + + {/* Tags */} + + Tags (comma-separated) + handleFieldChange('tags', value)} + placeholder="emergency, chest, pulmonary" + placeholderTextColor={theme.colors.textMuted} + /> + + + {/* Related Findings */} + {renderRelatedFindings()} + + {/* Submit Button */} + + {isSubmitting ? ( + Submitting... + ) : ( + Submit Suggestion + )} + + + + + {/* Date Picker */} + {showDatePicker && ( + + )} + + {/* Success Modal */} + + + {/* Toast Messages */} + + + ); + }; + +// ============================================================================ +// 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. + */ diff --git a/app/modules/AIPrediction/screens/index.ts b/app/modules/AIPrediction/screens/index.ts index 2a13828..4c7627e 100644 --- a/app/modules/AIPrediction/screens/index.ts +++ b/app/modules/AIPrediction/screens/index.ts @@ -6,6 +6,7 @@ */ export { default as AIPredictionsScreen } from './AIPredictionsScreen'; +export { default as AIPredictionDetailScreen } from './AIPredictionDetailScreen'; /* * End of File: index.ts diff --git a/app/modules/AIPrediction/services/aiPredictionAPI.ts b/app/modules/AIPrediction/services/aiPredictionAPI.ts index 1a131c1..c74460e 100644 --- a/app/modules/AIPrediction/services/aiPredictionAPI.ts +++ b/app/modules/AIPrediction/services/aiPredictionAPI.ts @@ -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; + 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; + 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 })); } }; diff --git a/app/navigation/MainTabNavigator.tsx b/app/navigation/MainTabNavigator.tsx index fd044e0..95e7737 100644 --- a/app/navigation/MainTabNavigator.tsx +++ b/app/navigation/MainTabNavigator.tsx @@ -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: { diff --git a/package-lock.json b/package-lock.json index 838064c..524f4dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index e3ff13a..228bc40 100644 --- a/package.json +++ b/package.json @@ -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",