after adding series details page give for client testing

This commit is contained in:
yashwin-foxy 2025-08-19 19:45:28 +05:30
parent 266de3b512
commit 70d4ec6690
28 changed files with 2977 additions and 617 deletions

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -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.

View File

@ -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,
},

View File

@ -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';

View File

@ -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,
},
});

View File

@ -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: {

View File

@ -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,
},

View File

@ -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,
},
});

View File

@ -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,
},
});

View File

@ -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>
);
};

View File

@ -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
// ============================================================================

View File

@ -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;

View File

@ -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,
},
});

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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

View File

@ -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: {