1327 lines
43 KiB
TypeScript
1327 lines
43 KiB
TypeScript
/*
|
|
* 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,
|
|
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';
|
|
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';
|
|
|
|
// Get screen dimensions
|
|
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
|
|
|
// ============================================================================
|
|
// 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, 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: '#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' },
|
|
];
|
|
|
|
// ============================================================================
|
|
// 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
|
|
* - Enhanced visual design with modern mobile styling
|
|
*/
|
|
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);
|
|
}, []);
|
|
|
|
/**
|
|
* Close all dropdowns
|
|
*/
|
|
const closeAllDropdowns = useCallback(() => {
|
|
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 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; bgColor?: string; icon?: 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)}
|
|
>
|
|
<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,
|
|
option.color && { color: option.color }
|
|
]}>
|
|
{option.label}
|
|
</Text>
|
|
</View>
|
|
{currentValue === option.value && (
|
|
<Icon name="check" size={16} color={theme.colors.primary} />
|
|
)}
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
), []);
|
|
|
|
/**
|
|
* 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="Finding Key"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
/>
|
|
<TextInput
|
|
style={[styles.input, styles.relatedFindingValue]}
|
|
value={finding.value}
|
|
onChangeText={(value) => handleUpdateRelatedFinding(index, 'value', value)}
|
|
placeholder="Finding Value"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
/>
|
|
</View>
|
|
<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}
|
|
>
|
|
<Icon name="plus" size={20} color={theme.colors.background} />
|
|
<Text style={styles.addButtonText}>Add Finding</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
), [formData.relatedFindings, handleUpdateRelatedFinding, handleRemoveRelatedFinding, handleAddRelatedFinding]);
|
|
|
|
// ============================================================================
|
|
// RENDER
|
|
// ============================================================================
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container}>
|
|
<StatusBar barStyle="dark-content" backgroundColor={theme.colors.background} />
|
|
|
|
<TouchableWithoutFeedback onPress={closeAllDropdowns}>
|
|
<>
|
|
{/* Enhanced Header */}
|
|
{renderHeader()}
|
|
|
|
<View style={styles.mainContent}>
|
|
<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 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
|
|
style={[styles.input, styles.disabledInput]}
|
|
value={formData.patientId}
|
|
editable={false}
|
|
placeholder="Patient ID"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
/>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 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}>Type</Text>
|
|
<View style={styles.dropdownContainer}>
|
|
<TouchableOpacity
|
|
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(
|
|
SUGGESTION_TYPE_OPTIONS,
|
|
formData.suggestionType,
|
|
(value) => handleDropdownSelect('suggestionType', value)
|
|
)}
|
|
</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.enhancedInput}
|
|
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.enhancedInput, 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>
|
|
</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.enhancedInput}
|
|
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.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(
|
|
PRIORITY_OPTIONS,
|
|
formData.priority,
|
|
(value) => handleDropdownSelect('priority', value)
|
|
)}
|
|
</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.enhancedInput}
|
|
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.enhancedInput}
|
|
value={formData.costEstimate}
|
|
onChangeText={(value) => handleFieldChange('costEstimate', value)}
|
|
placeholder="Enter cost"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
keyboardType="numeric"
|
|
/>
|
|
</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.enhancedInput}
|
|
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.enhancedDateInput}
|
|
onPress={() => setShowDatePicker(true)}
|
|
>
|
|
<Icon name="calendar" size={18} color={theme.colors.primary} />
|
|
<Text style={[
|
|
styles.dateText,
|
|
!formData.expiresAt && styles.placeholderText
|
|
]}>
|
|
{formData.expiresAt
|
|
? formData.expiresAt.toLocaleDateString()
|
|
: 'Select date'
|
|
}
|
|
</Text>
|
|
</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.enhancedInput}
|
|
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.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.enhancedInput, 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.enhancedInput}
|
|
value={formData.tags}
|
|
onChangeText={(value) => handleFieldChange('tags', value)}
|
|
placeholder="emergency, chest, pulmonary"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
/>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Related Findings */}
|
|
{renderRelatedFindings()}
|
|
|
|
{/* Submit Button */}
|
|
<TouchableOpacity
|
|
style={[styles.submitButton, isSubmitting && styles.submitButtonDisabled]}
|
|
onPress={handleSubmitSuggestion}
|
|
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 && (
|
|
<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: '#F8FAFC',
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingHorizontal: theme.spacing.md,
|
|
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,
|
|
position: 'relative',
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
padding: theme.spacing.md,
|
|
paddingBottom: theme.spacing.xl,
|
|
},
|
|
mainContent: {
|
|
flex: 1,
|
|
},
|
|
|
|
// Card Styles
|
|
patientCard: {
|
|
backgroundColor: theme.colors.background,
|
|
borderRadius: theme.borderRadius.large,
|
|
padding: theme.spacing.lg,
|
|
marginBottom: theme.spacing.lg,
|
|
shadowColor: '#000000',
|
|
shadowOffset: { width: 0, height: 1 },
|
|
shadowOpacity: 0.05,
|
|
shadowRadius: 4,
|
|
elevation: 2,
|
|
borderLeftWidth: 4,
|
|
borderLeftColor: theme.colors.primary,
|
|
},
|
|
formCard: {
|
|
backgroundColor: theme.colors.background,
|
|
borderRadius: theme.borderRadius.large,
|
|
padding: theme.spacing.lg,
|
|
marginBottom: theme.spacing.lg,
|
|
shadowColor: '#000000',
|
|
shadowOffset: { width: 0, height: 1 },
|
|
shadowOpacity: 0.05,
|
|
shadowRadius: 4,
|
|
elevation: 2,
|
|
},
|
|
cardHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: theme.spacing.md,
|
|
paddingBottom: theme.spacing.sm,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#E2E8F0',
|
|
},
|
|
cardTitle: {
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
marginLeft: theme.spacing.sm,
|
|
},
|
|
patientCardHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
patientCardTitle: {
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
marginLeft: theme.spacing.sm,
|
|
},
|
|
|
|
// Form Styles
|
|
formGroup: {
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
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,
|
|
},
|
|
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: '#F8FAFC',
|
|
color: theme.colors.textSecondary,
|
|
borderColor: '#E2E8F0',
|
|
},
|
|
textArea: {
|
|
height: 100,
|
|
textAlignVertical: 'top',
|
|
},
|
|
|
|
// Dropdown Styles
|
|
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,
|
|
},
|
|
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,
|
|
borderWidth: 1,
|
|
borderColor: '#E2E8F0',
|
|
borderRadius: theme.borderRadius.medium,
|
|
backgroundColor: '#FFFFFF',
|
|
marginTop: theme.spacing.xs,
|
|
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.md,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#F1F5F9',
|
|
},
|
|
dropdownOptionContent: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
flex: 1,
|
|
},
|
|
dropdownOptionIcon: {
|
|
marginRight: theme.spacing.sm,
|
|
},
|
|
dropdownOptionSelected: {
|
|
backgroundColor: '#F8FAFC',
|
|
},
|
|
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,
|
|
},
|
|
priorityIndicator: {
|
|
width: 12,
|
|
height: 12,
|
|
borderRadius: 6,
|
|
marginRight: theme.spacing.sm,
|
|
},
|
|
|
|
// Date Input Styles
|
|
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,
|
|
},
|
|
enhancedDateInput: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
borderWidth: 1,
|
|
borderColor: '#E2E8F0',
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingHorizontal: theme.spacing.md,
|
|
paddingVertical: theme.spacing.md,
|
|
backgroundColor: '#FFFFFF',
|
|
shadowColor: '#000000',
|
|
shadowOffset: { width: 0, height: 1 },
|
|
shadowOpacity: 0.03,
|
|
shadowRadius: 1,
|
|
elevation: 1,
|
|
},
|
|
dateText: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textPrimary,
|
|
flex: 1,
|
|
marginLeft: theme.spacing.sm,
|
|
},
|
|
placeholderText: {
|
|
color: theme.colors.textMuted,
|
|
},
|
|
helperText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textMuted,
|
|
marginTop: theme.spacing.xs,
|
|
fontStyle: 'italic',
|
|
},
|
|
|
|
// Section Styles
|
|
sectionHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
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',
|
|
alignItems: 'center',
|
|
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,
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingHorizontal: theme.spacing.md,
|
|
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,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
color: theme.colors.background,
|
|
},
|
|
removeButton: {
|
|
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.large,
|
|
paddingVertical: theme.spacing.lg,
|
|
alignItems: 'center',
|
|
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;
|
|
|
|
/*
|
|
* End of File: AIPredictionDetailScreen.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|