after adding series details page give for client testing
@ -71,6 +71,7 @@ def enableProguardInReleaseBuilds = false
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = true
|
||||
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
|
||||
|
||||
android {
|
||||
@ -79,6 +80,13 @@ android {
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
|
||||
namespace "com.neoscan_physician"
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
include 'armeabi-v7a', 'arm64-v8a', 'x86'
|
||||
universalApk false
|
||||
}
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId "com.neoscan_physician"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 24 KiB |
BIN
android/app/src/main/res/playstore.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
@ -32,7 +32,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=true
|
||||
newArchEnabled=false
|
||||
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
|
||||
@ -163,7 +163,7 @@ const styles = StyleSheet.create({
|
||||
textAlign: 'center',
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: theme.typography.bodyMedium,
|
||||
fontSize: theme.typography.fontSize.bodyMedium,
|
||||
color: theme.colors.textSecondary,
|
||||
textAlign: 'center',
|
||||
},
|
||||
@ -171,25 +171,25 @@ const styles = StyleSheet.create({
|
||||
marginBottom: theme.spacing.xl,
|
||||
},
|
||||
message: {
|
||||
fontSize: theme.typography.bodyMedium,
|
||||
fontSize: theme.typography.fontSize.bodyMedium,
|
||||
color: theme.colors.textPrimary,
|
||||
marginBottom: theme.spacing.md,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
optionsContainer: {
|
||||
marginLeft: theme.spacing.sm,
|
||||
},
|
||||
optionText: {
|
||||
fontSize: theme.typography.bodyMedium,
|
||||
fontSize: theme.typography.fontSize.bodyMedium,
|
||||
color: theme.colors.textSecondary,
|
||||
marginBottom: theme.spacing.xs,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
actions: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'column', // Changed from 'row' to 'column'
|
||||
gap: theme.spacing.md,
|
||||
},
|
||||
secondaryButton: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.background,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
@ -199,12 +199,11 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
},
|
||||
secondaryButtonText: {
|
||||
fontSize: theme.typography.bodyMedium,
|
||||
fontSize: theme.typography.fontSize.bodyMedium,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
color: theme.colors.textPrimary,
|
||||
},
|
||||
primaryButton: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.primary,
|
||||
borderRadius: theme.borderRadius.medium,
|
||||
paddingVertical: theme.spacing.md,
|
||||
@ -217,7 +216,7 @@ const styles = StyleSheet.create({
|
||||
elevation: 3,
|
||||
},
|
||||
primaryButtonText: {
|
||||
fontSize: theme.typography.bodyMedium,
|
||||
fontSize: theme.typography.fontSize.bodyMedium,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
color: theme.colors.background,
|
||||
},
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { logout, updateUserProfile } from './authSlice';
|
||||
import { authAPI } from '../services/authAPI';
|
||||
import { showError, showSuccess } from '../../../shared/utils/toast';
|
||||
import { showError, showSuccess, showWarning } from '../../../shared/utils/toast';
|
||||
|
||||
/**
|
||||
* Thunk to login user
|
||||
@ -31,6 +31,10 @@ export const login = createAsyncThunk(
|
||||
|
||||
if (response.ok && response.data && response.data.data) {
|
||||
// Return the user data for the fulfilled case
|
||||
if(response.data.data.user.dashboard_role !=='radiologist'){
|
||||
showWarning('You are not authorized to access this application')
|
||||
return rejectWithValue('Not Authorized');
|
||||
}
|
||||
return {...response.data.data.user,access_token:response.data.data.access_token};
|
||||
} else {
|
||||
const errorMessage = response.data?.message || response.problem || 'Unknown error';
|
||||
|
||||
@ -114,16 +114,15 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
textAlign: 'center',
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 16,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
textAlign: 'center',
|
||||
lineHeight: 24,
|
||||
marginBottom: theme.spacing.lg,
|
||||
@ -146,9 +145,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
retryText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: theme.colors.background,
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -267,12 +267,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
tabLabel: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
flex: 1,
|
||||
},
|
||||
tabLabelSelected: {
|
||||
fontWeight: '600',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
|
||||
// Count Badge Styles
|
||||
@ -293,7 +292,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
countText: {
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
textAlign: 'center',
|
||||
},
|
||||
countTextSelected: {
|
||||
|
||||
@ -358,7 +358,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
patientName: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.background,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
@ -448,7 +447,6 @@ const styles = StyleSheet.create({
|
||||
zoomText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
|
||||
|
||||
@ -139,16 +139,15 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
textAlign: 'center',
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 16,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
textAlign: 'center',
|
||||
lineHeight: 24,
|
||||
},
|
||||
@ -166,7 +165,7 @@ const styles = StyleSheet.create({
|
||||
smallText: {
|
||||
fontSize: 14,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.primary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -342,7 +342,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
patientName: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
@ -365,7 +364,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginLeft: 4,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
@ -381,7 +380,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
emergencyButtonText: {
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
color: theme.colors.background,
|
||||
marginLeft: 4,
|
||||
},
|
||||
@ -407,16 +406,16 @@ const styles = StyleSheet.create({
|
||||
color: theme.colors.textMuted,
|
||||
marginBottom: 2,
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: '500',
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
infoValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
color: theme.colors.textPrimary,
|
||||
textAlign: 'center',
|
||||
},
|
||||
modalityText: {
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
|
||||
// Institution Row
|
||||
@ -446,14 +445,14 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
seriesLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
color: theme.colors.textSecondary,
|
||||
marginLeft: 4,
|
||||
},
|
||||
seriesText: {
|
||||
fontSize: 14,
|
||||
color: theme.colors.textPrimary,
|
||||
fontWeight: '500',
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
|
||||
// Footer Section
|
||||
@ -484,7 +483,7 @@ const styles = StyleSheet.create({
|
||||
fontSize: 12,
|
||||
color: theme.colors.textSecondary,
|
||||
marginRight: theme.spacing.xs,
|
||||
fontWeight: '500',
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
|
||||
// Import screens
|
||||
import { PatientsScreen, PatientDetailsScreen } from '../screens';
|
||||
import { PatientsScreen, PatientDetailsScreen, SeriesDetailScreen } from '../screens';
|
||||
|
||||
// Import types
|
||||
import { PatientCareStackParamList } from './navigationTypes';
|
||||
@ -28,9 +28,10 @@ const Stack = createStackNavigator<PatientCareStackParamList>();
|
||||
* Screens:
|
||||
* - PatientsScreen: Main patient list screen
|
||||
* - PatientDetailsScreen: Detailed patient information and DICOM images
|
||||
* - SeriesDetailScreen: Detailed series information with predictions and feedback
|
||||
*
|
||||
* Navigation Flow:
|
||||
* PatientsScreen → PatientDetailsScreen (with patient data)
|
||||
* PatientsScreen → PatientDetailsScreen (with patient data) → SeriesDetailScreen (with series data)
|
||||
*/
|
||||
const PatientCareStackNavigator: React.FC = () => {
|
||||
return (
|
||||
@ -63,6 +64,17 @@ const PatientCareStackNavigator: React.FC = () => {
|
||||
gestureDirection: 'horizontal',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Series Detail Screen - Detailed series information with predictions and feedback */}
|
||||
<Stack.Screen
|
||||
name="SeriesDetail"
|
||||
component={SeriesDetailScreen}
|
||||
options={{
|
||||
title: 'Series Details',
|
||||
gestureEnabled: true,
|
||||
gestureDirection: 'horizontal',
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
@ -24,6 +24,9 @@ export type PatientCareStackParamList = {
|
||||
|
||||
// Patient Details Screen - Comprehensive patient information and DICOM images
|
||||
PatientDetails: PatientDetailsScreenParams;
|
||||
|
||||
// Series Detail Screen - Detailed series information with predictions and feedback
|
||||
SeriesDetail: SeriesDetailScreenParams;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -56,6 +59,30 @@ export interface PatientDetailsScreenParams {
|
||||
patientName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SeriesDetailScreenParams
|
||||
*
|
||||
* Purpose: Parameters for the series detail screen
|
||||
*
|
||||
* Parameters:
|
||||
* - patientId: Required patient ID for the series
|
||||
* - patientName: Required patient name for display
|
||||
* - seriesNumber: Required series number to display
|
||||
* - seriesData: Required series data object
|
||||
* - patientData: Required patient data object for context
|
||||
* - onFeedbackSubmitted: Optional callback to refresh parent screen data
|
||||
*/
|
||||
export interface SeriesDetailScreenParams {
|
||||
patientId: string;
|
||||
patientName: string;
|
||||
seriesNumber: string;
|
||||
seriesData: any;
|
||||
patientData: any;
|
||||
// Callback function to refresh parent screen data when feedback is submitted
|
||||
// This ensures PatientDetailsScreen shows updated information when user navigates back
|
||||
onFeedbackSubmitted?: () => void;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// NAVIGATION PROP TYPES
|
||||
// ============================================================================
|
||||
@ -91,6 +118,16 @@ export interface PatientDetailsScreenProps {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* SeriesDetailScreenProps - Props for SeriesDetailScreen component
|
||||
*/
|
||||
export interface SeriesDetailScreenProps {
|
||||
navigation: PatientCareNavigationProp;
|
||||
route: {
|
||||
params: SeriesDetailScreenParams;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// NAVIGATION UTILITY TYPES
|
||||
// ============================================================================
|
||||
|
||||
@ -37,6 +37,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { patientAPI } from '../services/patientAPI';
|
||||
import { selectUser } from '../../Auth/redux/authSelectors';
|
||||
import { API_CONFIG } from '../../../shared/utils';
|
||||
import { PatientDetailsScreenProps } from '../navigation/navigationTypes';
|
||||
|
||||
// Get screen dimensions
|
||||
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
||||
@ -45,15 +46,7 @@ const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
||||
// INTERFACES
|
||||
// ============================================================================
|
||||
|
||||
interface PatientDetailsScreenProps {
|
||||
navigation: any;
|
||||
route: {
|
||||
params: {
|
||||
patientId: string;
|
||||
patientName?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
interface PatientInfo {
|
||||
name: string;
|
||||
@ -149,21 +142,8 @@ const PatientDetailsScreen: React.FC<PatientDetailsScreenProps> = ({ navigation,
|
||||
const [showFullImage, setShowFullImage] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'aiAnalysis' | 'history'>('overview');
|
||||
|
||||
// Feedback state
|
||||
const [showFeedbackModal, setShowFeedbackModal] = useState(false);
|
||||
const [selectedSeriesForFeedback, setSelectedSeriesForFeedback] = useState<SeriesSummary | null>(null);
|
||||
const [selectedPrediction, setSelectedPrediction] = useState<Prediction | null>(null);
|
||||
const [feedbackText, setFeedbackText] = useState('');
|
||||
const [isPositive, setIsPositive] = useState<boolean | null>(null);
|
||||
const [isSubmittingFeedback, setIsSubmittingFeedback] = useState(false);
|
||||
|
||||
// Feedback result modal state
|
||||
const [showFeedbackResultModal, setShowFeedbackResultModal] = useState(false);
|
||||
const [feedbackResult, setFeedbackResult] = useState<{
|
||||
type: 'success' | 'error';
|
||||
title: string;
|
||||
message: string;
|
||||
} | null>(null);
|
||||
// Navigation state
|
||||
const [selectedSeriesForDetail, setSelectedSeriesForDetail] = useState<SeriesSummary | null>(null);
|
||||
|
||||
// ============================================================================
|
||||
// DATA FETCHING
|
||||
@ -348,115 +328,29 @@ const PatientDetailsScreen: React.FC<PatientDetailsScreenProps> = ({ navigation,
|
||||
}, [navigation]);
|
||||
|
||||
// ============================================================================
|
||||
// FEEDBACK HANDLERS
|
||||
// NAVIGATION HANDLERS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Handle Open Feedback Modal
|
||||
* Handle Navigate to Series Detail
|
||||
*
|
||||
* Purpose: Open feedback modal for a specific series and prediction
|
||||
* Purpose: Navigate to detailed series view
|
||||
*
|
||||
* @param series - Series data for feedback
|
||||
* @param prediction - Prediction data for feedback
|
||||
* @param series - Series data to view in detail
|
||||
*/
|
||||
const handleOpenFeedback = useCallback((series: SeriesSummary, prediction: Prediction) => {
|
||||
setSelectedSeriesForFeedback(series);
|
||||
setSelectedPrediction(prediction);
|
||||
setFeedbackText('');
|
||||
setIsPositive(null);
|
||||
setShowFeedbackModal(true);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Handle Submit Feedback
|
||||
*
|
||||
* Purpose: Submit feedback to API
|
||||
*/
|
||||
const handleSubmitFeedback = useCallback(async () => {
|
||||
if ( !selectedPrediction || !feedbackText.trim() || isPositive === null) {
|
||||
setFeedbackResult({
|
||||
type: 'error',
|
||||
title: 'Validation Error',
|
||||
message: 'Please provide all required feedback information'
|
||||
});
|
||||
setShowFeedbackResultModal(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmittingFeedback(true);
|
||||
|
||||
if (!patientData?.patid) {
|
||||
throw new Error('Patient ID not available');
|
||||
}
|
||||
|
||||
const feedbackPayload = {
|
||||
patid: patientData.patid,
|
||||
prediction_id: selectedPrediction.id,
|
||||
feedback_text: feedbackText.trim(),
|
||||
is_positive: isPositive
|
||||
};
|
||||
|
||||
console.log('Submitting feedback payload:', feedbackPayload);
|
||||
|
||||
// Call the actual API
|
||||
const response = await patientAPI.submitFeedback(feedbackPayload, user?.access_token);
|
||||
console.log('update response', response);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(response.problem || 'Failed to submit feedback');
|
||||
}
|
||||
|
||||
// Show success message
|
||||
setFeedbackResult({
|
||||
type: 'success',
|
||||
title: 'Feedback Submitted',
|
||||
message: 'Your feedback has been recorded successfully.'
|
||||
});
|
||||
setShowFeedbackResultModal(true);
|
||||
} catch (error: any) {
|
||||
setFeedbackResult({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
message: error.message || 'Failed to submit feedback. Please try again.'
|
||||
});
|
||||
setShowFeedbackResultModal(true);
|
||||
} finally {
|
||||
setIsSubmittingFeedback(false);
|
||||
}
|
||||
}, [selectedSeriesForFeedback, selectedPrediction, feedbackText, isPositive, patientData?.patid]);
|
||||
|
||||
/**
|
||||
* Handle Close Feedback Modal
|
||||
*
|
||||
* Purpose: Close feedback modal and reset state
|
||||
*/
|
||||
const handleCloseFeedback = useCallback(() => {
|
||||
setShowFeedbackModal(false);
|
||||
setSelectedSeriesForFeedback(null);
|
||||
setSelectedPrediction(null);
|
||||
setFeedbackText('');
|
||||
setIsPositive(null);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Handle Feedback Result Modal Close
|
||||
*
|
||||
* Purpose: Close feedback result modal and reset form if success
|
||||
*/
|
||||
const handleFeedbackResultClose = useCallback(() => {
|
||||
setShowFeedbackResultModal(false);
|
||||
setFeedbackResult(null);
|
||||
const handleNavigateToSeriesDetail = useCallback((series: SeriesSummary) => {
|
||||
if (!patientData) return;
|
||||
|
||||
// If it was a success, also close the feedback modal and reset form
|
||||
if (feedbackResult?.type === 'success') {
|
||||
setShowFeedbackModal(false);
|
||||
setSelectedSeriesForFeedback(null);
|
||||
setSelectedPrediction(null);
|
||||
setFeedbackText('');
|
||||
setIsPositive(null);
|
||||
}
|
||||
}, [feedbackResult?.type]);
|
||||
navigation.navigate('SeriesDetail', {
|
||||
patientId: patientData.patid,
|
||||
patientName: patientData.patient_info.name,
|
||||
seriesNumber: series.series_num,
|
||||
seriesData: series,
|
||||
patientData: patientData,
|
||||
// Pass the refresh function as callback so parent screen can update when feedback is submitted
|
||||
onFeedbackSubmitted: fetchPatientData
|
||||
});
|
||||
}, [navigation, patientData, fetchPatientData]);
|
||||
|
||||
// ============================================================================
|
||||
// UTILITY FUNCTIONS
|
||||
@ -810,12 +704,12 @@ const PatientDetailsScreen: React.FC<PatientDetailsScreenProps> = ({ navigation,
|
||||
Series {series.series_num}: {series.series_description}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.feedbackButton}
|
||||
onPress={() => handleOpenFeedback(series, seriesPredictions[0])}
|
||||
style={styles.seriesDetailButton}
|
||||
onPress={() => handleNavigateToSeriesDetail(series)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Icon name="message-circle" size={16} color={theme.colors.primary} />
|
||||
<Text style={styles.feedbackButtonText}>Feedback</Text>
|
||||
<Icon name="external-link" size={16} color={theme.colors.primary} />
|
||||
<Text style={styles.seriesDetailButtonText}>Series Details</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Text style={styles.seriesMeta}>
|
||||
@ -1098,150 +992,8 @@ const PatientDetailsScreen: React.FC<PatientDetailsScreenProps> = ({ navigation,
|
||||
{activeTab === 'history' && renderHistoryTab()}
|
||||
</ScrollView>
|
||||
|
||||
{/* Feedback Modal: Allows physicians to provide clinical feedback on AI predictions and DICOM images */}
|
||||
{showFeedbackModal && selectedSeriesForFeedback && (
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.feedbackModal}>
|
||||
<View style={styles.modalHeader}>
|
||||
<Text style={styles.modalTitle}>Provide Feedback</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.closeButton}
|
||||
onPress={handleCloseFeedback}
|
||||
>
|
||||
<Icon name="x" size={24} color={theme.colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.modalContent}>
|
||||
|
||||
{/* <View style={styles.feedbackSeriesInfo}>
|
||||
<Text style={styles.feedbackSeriesTitle}>
|
||||
Series {selectedSeriesForFeedback.series_num}: {selectedSeriesForFeedback.series_description}
|
||||
</Text>
|
||||
<Text style={styles.feedbackSeriesMeta}>
|
||||
{selectedSeriesForFeedback.modality} • {selectedSeriesForFeedback.total_images} images
|
||||
</Text>
|
||||
</View> */}
|
||||
|
||||
{/* Series and Prediction Info */}
|
||||
{selectedPrediction && (
|
||||
<View style={styles.feedbackPredictionInfo}>
|
||||
<Text style={styles.feedbackPredictionTitle}>
|
||||
AI Prediction: {selectedPrediction.prediction.label}
|
||||
</Text>
|
||||
<Text style={styles.feedbackPredictionMeta}>
|
||||
Confidence: {(selectedPrediction.prediction.confidence_score * 100).toFixed(1)}% •
|
||||
Type: {selectedPrediction.prediction.finding_type}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Prediction Accuracy Selection */}
|
||||
<View style={styles.feedbackSection}>
|
||||
<Text style={styles.feedbackSectionTitle}>Is this prediction accurate?</Text>
|
||||
<View style={styles.predictionAccuracyContainer}>
|
||||
{[
|
||||
{ key: 'true', label: 'Yes (Positive)', color: theme.colors.success, icon: 'check-circle', value: true },
|
||||
{ key: 'false', label: 'No (Negative)', color: theme.colors.error, icon: 'x-circle', value: false }
|
||||
].map((option) => (
|
||||
<TouchableOpacity
|
||||
key={option.key}
|
||||
onPress={() => setIsPositive(option.value)}
|
||||
style={[
|
||||
styles.predictionAccuracyButton,
|
||||
isPositive === option.value && styles.predictionAccuracyButtonActive,
|
||||
{ borderColor: option.color }
|
||||
]}
|
||||
>
|
||||
<Icon
|
||||
name={option.icon as any}
|
||||
size={16}
|
||||
color={isPositive === option.value ? theme.colors.background : option.color}
|
||||
/>
|
||||
<Text style={[
|
||||
styles.predictionAccuracyButtonText,
|
||||
isPositive === option.value && styles.predictionAccuracyButtonTextActive
|
||||
]}>
|
||||
{option.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Feedback Text Input */}
|
||||
<View style={styles.feedbackSection}>
|
||||
<Text style={styles.feedbackSectionTitle}>Your Feedback</Text>
|
||||
<TextInput
|
||||
style={styles.feedbackTextInput}
|
||||
placeholder="Provide your clinical insights, suggestions, or corrections..."
|
||||
placeholderTextColor={theme.colors.textMuted}
|
||||
value={feedbackText}
|
||||
onChangeText={setFeedbackText}
|
||||
multiline
|
||||
numberOfLines={4}
|
||||
textAlignVertical="top"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.modalFooter}>
|
||||
<TouchableOpacity
|
||||
style={styles.cancelButton}
|
||||
onPress={handleCloseFeedback}
|
||||
>
|
||||
<Text style={styles.cancelButtonText}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.submitButton,
|
||||
(!feedbackText.trim() || isPositive === null || isSubmittingFeedback) && styles.submitButtonDisabled
|
||||
]}
|
||||
onPress={handleSubmitFeedback}
|
||||
disabled={!feedbackText.trim() || isPositive === null || isSubmittingFeedback}
|
||||
>
|
||||
<Text style={styles.submitButtonText}>Submit Feedback</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Feedback Result Modal: Shows success/error messages for feedback submission */}
|
||||
{showFeedbackResultModal && feedbackResult && (
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.feedbackResultModal}>
|
||||
<View style={styles.modalHeader}>
|
||||
<Icon
|
||||
name={feedbackResult.type === 'success' ? 'check-circle' : 'alert-circle'}
|
||||
size={24}
|
||||
color={feedbackResult.type === 'success' ? theme.colors.success : theme.colors.error}
|
||||
/>
|
||||
<Text style={[
|
||||
styles.modalTitle,
|
||||
{ color: feedbackResult.type === 'success' ? theme.colors.success : theme.colors.error }
|
||||
]}>
|
||||
{feedbackResult.title}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.modalContent}>
|
||||
<Text style={styles.feedbackResultMessage}>
|
||||
{feedbackResult.message}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.modalFooter}>
|
||||
<TouchableOpacity
|
||||
style={styles.okButton}
|
||||
onPress={handleFeedbackResultClose}
|
||||
>
|
||||
<Text style={styles.okButtonText}>OK</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
@ -1276,7 +1028,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
headerTitleText: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
@ -1320,7 +1071,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
patientName: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
@ -1348,7 +1098,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
color: theme.colors.background,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
@ -1368,7 +1118,7 @@ const styles = StyleSheet.create({
|
||||
emergencyButtonText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginLeft: 8,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
@ -1413,7 +1163,7 @@ const styles = StyleSheet.create({
|
||||
tabCountText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
|
||||
// Content Styles
|
||||
@ -1430,7 +1180,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.md,
|
||||
@ -1490,11 +1239,10 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
seriesTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
feedbackButton: {
|
||||
seriesDetailButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.colors.backgroundAlt,
|
||||
@ -1504,7 +1252,7 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
},
|
||||
feedbackButtonText: {
|
||||
seriesDetailButtonText: {
|
||||
fontSize: 12,
|
||||
color: theme.colors.primary,
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
@ -1549,7 +1297,7 @@ const styles = StyleSheet.create({
|
||||
imageNumber: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
textAlign: 'center',
|
||||
},
|
||||
|
||||
@ -1580,7 +1328,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
emptyStateTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginTop: theme.spacing.md,
|
||||
@ -1648,7 +1395,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
predictionSeriesTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.sm,
|
||||
@ -1672,7 +1418,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
predictionLabel: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
@ -1683,7 +1428,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
urgencyText: {
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
color: theme.colors.background,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
@ -1737,7 +1482,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
errorTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginTop: theme.spacing.md,
|
||||
@ -1764,7 +1508,6 @@ const styles = StyleSheet.create({
|
||||
retryButtonText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
|
||||
@ -1781,7 +1524,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
imageSectionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.sm,
|
||||
@ -1791,7 +1533,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
predictionsSectionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.sm,
|
||||
@ -1835,286 +1576,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
summaryValue: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginTop: theme.spacing.xs,
|
||||
},
|
||||
|
||||
// Feedback Modal Styles
|
||||
modalOverlay: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 1000,
|
||||
},
|
||||
feedbackModal: {
|
||||
backgroundColor: theme.colors.background,
|
||||
borderRadius: 12,
|
||||
width: '90%',
|
||||
maxWidth: 450,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 10,
|
||||
elevation: 10,
|
||||
},
|
||||
modalHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: theme.spacing.md,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: theme.colors.border,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
closeButton: {
|
||||
padding: theme.spacing.sm,
|
||||
},
|
||||
modalContent: {
|
||||
padding: theme.spacing.md,
|
||||
},
|
||||
feedbackSeriesInfo: {
|
||||
marginBottom: theme.spacing.md,
|
||||
},
|
||||
feedbackSeriesTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.xs,
|
||||
},
|
||||
feedbackSeriesMeta: {
|
||||
fontSize: 12,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
feedbackSection: {
|
||||
marginBottom: theme.spacing.md,
|
||||
},
|
||||
feedbackSectionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
feedbackTypeContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
feedbackTypeButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: theme.spacing.sm,
|
||||
paddingHorizontal: theme.spacing.md,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
},
|
||||
feedbackTypeButtonActive: {
|
||||
borderColor: theme.colors.primary,
|
||||
backgroundColor: theme.colors.primary,
|
||||
},
|
||||
feedbackTypeButtonText: {
|
||||
fontSize: 14,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
marginLeft: theme.spacing.sm,
|
||||
},
|
||||
feedbackTypeButtonTextActive: {
|
||||
color: theme.colors.background,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
priorityContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
priorityButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: theme.spacing.sm,
|
||||
paddingHorizontal: theme.spacing.md,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
},
|
||||
priorityButtonActive: {
|
||||
borderColor: theme.colors.primary,
|
||||
backgroundColor: theme.colors.primary,
|
||||
},
|
||||
priorityIndicator: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: 5,
|
||||
marginRight: theme.spacing.sm,
|
||||
},
|
||||
priorityButtonText: {
|
||||
fontSize: 14,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
},
|
||||
priorityButtonTextActive: {
|
||||
color: theme.colors.background,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
feedbackTextInput: {
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
borderRadius: 8,
|
||||
padding: theme.spacing.md,
|
||||
fontSize: 14,
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
minHeight: 100,
|
||||
textAlignVertical: 'top',
|
||||
},
|
||||
modalFooter: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
padding: theme.spacing.md,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: theme.colors.border,
|
||||
},
|
||||
|
||||
// Feedback Prediction Info Styles
|
||||
feedbackPredictionInfo: {
|
||||
// marginTop: theme.spacing.sm,
|
||||
paddingTop: theme.spacing.md,
|
||||
marginBottom: theme.spacing.sm,
|
||||
// borderTopWidth: 1,
|
||||
// borderTopColor: theme.colors.border,
|
||||
},
|
||||
feedbackPredictionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
marginBottom: theme.spacing.xs,
|
||||
},
|
||||
feedbackPredictionMeta: {
|
||||
fontSize: 12,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
},
|
||||
|
||||
// Prediction Accuracy Selection Styles
|
||||
predictionAccuracyContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
predictionAccuracyButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: theme.spacing.sm,
|
||||
paddingHorizontal: theme.spacing.md,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
minWidth: 120,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
predictionAccuracyButtonActive: {
|
||||
borderColor: theme.colors.primary,
|
||||
backgroundColor: theme.colors.primary,
|
||||
},
|
||||
predictionAccuracyButtonText: {
|
||||
fontSize: 14,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
marginLeft: theme.spacing.sm,
|
||||
},
|
||||
predictionAccuracyButtonTextActive: {
|
||||
color: theme.colors.background,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
cancelButton: {
|
||||
paddingVertical: theme.spacing.md,
|
||||
paddingHorizontal: theme.spacing.lg,
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
},
|
||||
cancelButtonText: {
|
||||
fontSize: 16,
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
},
|
||||
submitButton: {
|
||||
paddingVertical: theme.spacing.md,
|
||||
paddingHorizontal: theme.spacing.lg,
|
||||
borderRadius: 8,
|
||||
backgroundColor: theme.colors.primary,
|
||||
shadowColor: theme.colors.primary,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
},
|
||||
submitButtonDisabled: {
|
||||
backgroundColor: theme.colors.textMuted,
|
||||
opacity: 0.7,
|
||||
},
|
||||
submitButtonText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
|
||||
// Feedback Result Modal Styles
|
||||
feedbackResultModal: {
|
||||
backgroundColor: theme.colors.background,
|
||||
borderRadius: 16,
|
||||
padding: 0,
|
||||
width: '90%',
|
||||
maxWidth: 400,
|
||||
shadowColor: '#000000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.15,
|
||||
shadowRadius: 8,
|
||||
elevation: 8,
|
||||
},
|
||||
feedbackResultMessage: {
|
||||
fontSize: 16,
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
textAlign: 'center',
|
||||
lineHeight: 24,
|
||||
paddingHorizontal: theme.spacing.md,
|
||||
},
|
||||
okButton: {
|
||||
paddingVertical: theme.spacing.md,
|
||||
paddingHorizontal: theme.spacing.xl,
|
||||
borderRadius: 8,
|
||||
backgroundColor: theme.colors.primary,
|
||||
shadowColor: theme.colors.primary,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
minWidth: 100,
|
||||
alignItems: 'center',
|
||||
},
|
||||
okButtonText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
});
|
||||
|
||||
export default PatientDetailsScreen;
|
||||
|
||||
@ -215,7 +215,7 @@ const PatientsScreen: React.FC = () => {
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.headerRight}>
|
||||
{/* <View style={styles.headerRight}>
|
||||
<TouchableOpacity
|
||||
style={styles.actionButton}
|
||||
onPress={() => {
|
||||
@ -233,7 +233,7 @@ const PatientsScreen: React.FC = () => {
|
||||
>
|
||||
<Text style={styles.actionButtonText}>Filter</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View> */}
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -365,13 +365,6 @@ const PatientsScreen: React.FC = () => {
|
||||
tintColor={theme.colors.primary}
|
||||
/>
|
||||
}
|
||||
ListFooterComponent={
|
||||
<View style={styles.listFooter}>
|
||||
<Text style={styles.footerText}>
|
||||
Showing {filteredPatients.length} of {patients.length} patients
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -404,6 +397,7 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: theme.colors.background,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: theme.colors.border,
|
||||
marginBottom: theme.spacing.md,
|
||||
},
|
||||
headerLeft: {
|
||||
flex: 1,
|
||||
@ -414,7 +408,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.textPrimary,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
},
|
||||
@ -434,7 +427,6 @@ const styles = StyleSheet.create({
|
||||
actionButtonText: {
|
||||
color: theme.colors.textSecondary,
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
},
|
||||
|
||||
@ -478,7 +470,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
errorTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.error,
|
||||
marginBottom: theme.spacing.sm,
|
||||
fontFamily: theme.typography.fontFamily.bold,
|
||||
@ -502,7 +493,6 @@ const styles = StyleSheet.create({
|
||||
retryButtonText: {
|
||||
color: theme.colors.background,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
},
|
||||
});
|
||||
|
||||
2846
app/modules/PatientCare/screens/SeriesDetailScreen.tsx
Normal file
@ -7,6 +7,7 @@
|
||||
|
||||
export { default as PatientsScreen } from './PatientsScreen';
|
||||
export { default as PatientDetailsScreen } from './PatientDetailsScreen';
|
||||
export { default as SeriesDetailScreen } from './SeriesDetailScreen';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
|
||||
@ -66,11 +66,12 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: theme.colors.background,
|
||||
paddingHorizontal: theme.spacing.md,
|
||||
paddingVertical: theme.spacing.lg,
|
||||
paddingVertical: theme.spacing.md,
|
||||
borderBottomColor: theme.colors.border,
|
||||
borderBottomWidth: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing.md,
|
||||
},
|
||||
|
||||
// Back button styling
|
||||
|
||||
@ -36,6 +36,7 @@ import {
|
||||
selectUserProfilePhoto,
|
||||
selectDashboardSettings
|
||||
} from '../../Auth/redux/authSelectors';
|
||||
import { API_CONFIG } from '../../../shared/utils';
|
||||
|
||||
/**
|
||||
* SettingsScreenProps Interface
|
||||
@ -332,7 +333,7 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
||||
};
|
||||
|
||||
|
||||
|
||||
console.log('user', user)
|
||||
// ============================================================================
|
||||
// MAIN RENDER
|
||||
// ============================================================================
|
||||
@ -363,7 +364,7 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
||||
<View style={styles.profileImageContainer}>
|
||||
{user.profile_photo_url ? (
|
||||
<Image
|
||||
source={{ uri: user.profile_photo_url }}
|
||||
source={{ uri: API_CONFIG.BASE_URL + '/api/auth' + user.profile_photo_url }}
|
||||
style={styles.profileImage}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
@ -479,6 +480,7 @@ const styles = StyleSheet.create({
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 30,
|
||||
backgroundColor:theme.colors.primary,
|
||||
},
|
||||
|
||||
fallbackAvatar: {
|
||||
|
||||