navigation isue resolved
This commit is contained in:
parent
41de1bbb25
commit
0a95550230
@ -126,11 +126,14 @@ export const PredictionCard: React.FC<PredictionCardProps> = ({
|
||||
{/* Header Section */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.patientInfo}>
|
||||
<Text style={styles.patientName}>
|
||||
<Text style={styles.patientName} numberOfLines={1}>
|
||||
{prediction.patientdetails.Name || 'Unknown Patient'}
|
||||
</Text>
|
||||
<Text style={styles.patientDetails}>
|
||||
{prediction.patientdetails.PatID} • {prediction.patientdetails.PatAge} • {prediction.patientdetails.PatSex}
|
||||
<Text style={styles.patientDetails} numberOfLines={1} ellipsizeMode="head">
|
||||
{prediction.patientdetails.PatID}
|
||||
</Text>
|
||||
<Text style={styles.patientDetailsSecondary}>
|
||||
{prediction.patientdetails.PatAge} • {prediction.patientdetails.PatSex}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@ -255,7 +258,12 @@ const styles = StyleSheet.create({
|
||||
borderRadius: theme.borderRadius.large,
|
||||
padding: theme.spacing.md,
|
||||
marginBottom: theme.spacing.md,
|
||||
...theme.shadows.primary,
|
||||
// Custom shadow properties to ensure visibility
|
||||
shadowColor: '#000000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
|
||||
header: {
|
||||
@ -278,6 +286,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
|
||||
patientDetails: {
|
||||
fontSize: theme.typography.fontSize.bodySmall,
|
||||
fontFamily: theme.typography.fontFamily.medium,
|
||||
color: theme.colors.textPrimary,
|
||||
marginBottom: theme.spacing.xs,
|
||||
},
|
||||
patientDetailsSecondary: {
|
||||
fontSize: theme.typography.fontSize.bodySmall,
|
||||
fontFamily: theme.typography.fontFamily.regular,
|
||||
color: theme.colors.textSecondary,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
@ -44,8 +44,14 @@ interface PredictionsListProps {
|
||||
* - Loading states
|
||||
* - Error handling
|
||||
* - Empty states
|
||||
*
|
||||
* Performance Optimizations:
|
||||
* - Both datasets (with/without feedback) are pre-loaded upfront
|
||||
* - Tab switching is instant - no filtering needed
|
||||
* - React.memo prevents unnecessary re-renders
|
||||
* - useCallback optimizes event handlers
|
||||
*/
|
||||
export const PredictionsList: React.FC<PredictionsListProps> = ({
|
||||
export const PredictionsList: React.FC<PredictionsListProps> = React.memo(({
|
||||
onPredictionPress,
|
||||
}) => {
|
||||
const {
|
||||
@ -57,6 +63,14 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
|
||||
refreshPredictions,
|
||||
} = usePredictions();
|
||||
|
||||
// Performance optimization: Memoize feedback count calculation
|
||||
const feedbackCount = useMemo(() => {
|
||||
if (activeTab === 'with-feedback') {
|
||||
return currentPredictions.filter(p => p.feedbacks?.length > 0).length;
|
||||
}
|
||||
return 0;
|
||||
}, [activeTab, currentPredictions]);
|
||||
|
||||
// ============================================================================
|
||||
// UTILITY FUNCTIONS
|
||||
// ============================================================================
|
||||
@ -66,18 +80,18 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
|
||||
*
|
||||
* Purpose: Switch between radiologist feedback status tabs
|
||||
*/
|
||||
const handleTabSwitch = (tab: 'with-feedback' | 'without-feedback') => {
|
||||
const handleTabSwitch = useCallback((tab: 'with-feedback' | 'without-feedback') => {
|
||||
switchTab(tab);
|
||||
};
|
||||
}, [switchTab]);
|
||||
|
||||
/**
|
||||
* Handle Prediction Press
|
||||
*
|
||||
* Purpose: Handle when a prediction card is pressed
|
||||
*/
|
||||
const handlePredictionPress = (prediction: PredictionData) => {
|
||||
const handlePredictionPress = useCallback((prediction: PredictionData) => {
|
||||
onPredictionPress(prediction);
|
||||
};
|
||||
}, [onPredictionPress]);
|
||||
|
||||
/**
|
||||
* Render Tab Button
|
||||
@ -150,15 +164,15 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
|
||||
// RENDER
|
||||
// ============================================================================
|
||||
|
||||
// Debug logging to see current state
|
||||
console.log('🔍 PredictionsList render debug:');
|
||||
console.log('Active tab:', activeTab);
|
||||
console.log('Current predictions count:', currentPredictions.length);
|
||||
console.log('Current predictions sample:', currentPredictions.slice(0, 2).map(p => ({
|
||||
id: p.id,
|
||||
has_feedback: p.has_provided_feedback,
|
||||
feedbacks_count: p.feedbacks?.length || 0
|
||||
})));
|
||||
// Performance optimization: Only log when needed (development mode)
|
||||
// Note: Tab switching is now instant due to pre-loaded datasets
|
||||
if (__DEV__ && currentPredictions.length > 0) {
|
||||
console.log('🔍 PredictionsList render debug:', {
|
||||
activeTab,
|
||||
count: currentPredictions.length,
|
||||
performance: 'Instant tab switching - datasets pre-loaded'
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@ -184,46 +198,48 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
|
||||
{currentPredictions.length} prediction{currentPredictions.length !== 1 ? 's' : ''} found
|
||||
{activeTab === 'with-feedback' && (
|
||||
<Text style={styles.feedbackCount}>
|
||||
{' • '}{currentPredictions.filter(p => p.feedbacks && p.feedbacks.length > 0).length} with feedback
|
||||
{' • '}{feedbackCount} with feedback
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Horizontal Scrolling Predictions */}
|
||||
<FlatList
|
||||
data={currentPredictions}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.predictionCardWrapper}>
|
||||
<PredictionCard
|
||||
prediction={item}
|
||||
onPress={() => handlePredictionPress(item)}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsHorizontalScrollIndicator={true}
|
||||
showsVerticalScrollIndicator={false}
|
||||
horizontal={true}
|
||||
scrollEnabled={true}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={currentLoadingState}
|
||||
onRefresh={refreshPredictions}
|
||||
colors={[theme.colors.primary]}
|
||||
tintColor={theme.colors.primary}
|
||||
/>
|
||||
}
|
||||
ListEmptyComponent={renderEmptyState}
|
||||
/>
|
||||
{/* Horizontal Scrolling Predictions */}
|
||||
<View style={styles.listWrapper}>
|
||||
<FlatList
|
||||
data={currentPredictions}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.predictionCardWrapper}>
|
||||
<PredictionCard
|
||||
prediction={item}
|
||||
onPress={() => handlePredictionPress(item)}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
horizontal={true}
|
||||
scrollEnabled={true}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={currentLoadingState}
|
||||
onRefresh={refreshPredictions}
|
||||
colors={[theme.colors.primary]}
|
||||
tintColor={theme.colors.primary}
|
||||
/>
|
||||
}
|
||||
ListEmptyComponent={renderEmptyState}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// STYLES
|
||||
@ -271,10 +287,19 @@ const styles = StyleSheet.create({
|
||||
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
|
||||
},
|
||||
|
||||
listWrapper: {
|
||||
// flex: 1,
|
||||
minHeight: 0, // Prevent flex overflow
|
||||
marginBottom: theme.spacing.md, // Add bottom margin for shadow visibility
|
||||
},
|
||||
|
||||
listContainer: {
|
||||
padding: theme.spacing.md,
|
||||
padding: 2,
|
||||
paddingBottom: theme.spacing.xxl, // Increased bottom padding for shadow visibility
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
|
||||
@ -366,7 +391,9 @@ const styles = StyleSheet.create({
|
||||
|
||||
predictionCardWrapper: {
|
||||
marginRight: theme.spacing.md,
|
||||
marginBottom: theme.spacing.md, // Add bottom margin for shadow visibility
|
||||
width: 280, // Uniform width for all cards
|
||||
// Height will be determined by content naturally
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -53,12 +53,14 @@ export const usePredictions = () => {
|
||||
const activeTab = useAppSelector(selectActiveTab);
|
||||
const searchQuery = useAppSelector(selectSearchQuery);
|
||||
|
||||
// Performance optimization: Load both datasets upfront
|
||||
const predictionsWithFeedback = useAppSelector(selectPredictionsWithFeedback);
|
||||
const predictionsWithoutFeedback = useAppSelector(selectPredictionsWithoutFeedback);
|
||||
|
||||
const isLoading = useAppSelector(selectIsLoading);
|
||||
const error = useAppSelector(selectError);
|
||||
|
||||
// Performance optimization: Direct dataset selection - no filtering needed
|
||||
const currentPredictions = useAppSelector(selectCurrentPredictions);
|
||||
const currentLoadingState = useAppSelector(selectCurrentLoadingState);
|
||||
const currentError = useAppSelector(selectCurrentError);
|
||||
@ -74,6 +76,7 @@ export const usePredictions = () => {
|
||||
* Switch Active Tab
|
||||
*
|
||||
* Purpose: Change between feedback tabs
|
||||
* Performance: Instant switching - no filtering needed as both datasets are pre-loaded
|
||||
*/
|
||||
const switchTab = useCallback((tab: PredictionTabType) => {
|
||||
dispatch(setActiveTab(tab));
|
||||
@ -132,12 +135,14 @@ export const usePredictions = () => {
|
||||
|
||||
/**
|
||||
* Auto-fetch data when component mounts or token changes
|
||||
* Performance optimization: Load both datasets upfront to eliminate tab switching delays
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!authToken) return;
|
||||
|
||||
// Only fetch if we don't have any predictions yet
|
||||
if (predictionsWithFeedback.length === 0 && predictionsWithoutFeedback.length === 0) {
|
||||
// Performance optimization: Always fetch if either dataset is empty
|
||||
// This ensures both datasets are loaded upfront for instant tab switching
|
||||
if (predictionsWithFeedback.length === 0 || predictionsWithoutFeedback.length === 0) {
|
||||
dispatch(fetchAllPredictions(authToken));
|
||||
}
|
||||
}, [authToken, predictionsWithFeedback.length, predictionsWithoutFeedback.length, dispatch]);
|
||||
|
||||
@ -218,9 +218,16 @@ export const selectError = (state: { predictions: PredictionsState }) => state.p
|
||||
|
||||
export const selectCurrentPredictions = (state: { predictions: PredictionsState }) => {
|
||||
const { activeTab, predictionsWithFeedback, predictionsWithoutFeedback } = state.predictions;
|
||||
// Performance optimization: Direct dataset selection instead of filtering
|
||||
return activeTab === 'with-feedback' ? predictionsWithFeedback : predictionsWithoutFeedback;
|
||||
};
|
||||
|
||||
// Performance optimization: Pre-computed selectors for instant tab switching
|
||||
export const selectPredictionsForTab = (state: { predictions: PredictionsState }, tab: PredictionTabType) => {
|
||||
const { predictionsWithFeedback, predictionsWithoutFeedback } = state.predictions;
|
||||
return tab === 'with-feedback' ? predictionsWithFeedback : predictionsWithoutFeedback;
|
||||
};
|
||||
|
||||
export const selectCurrentLoadingState = (state: { predictions: PredictionsState }) => {
|
||||
return state.predictions.isLoading;
|
||||
};
|
||||
|
||||
@ -638,9 +638,8 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
|
||||
// Navigate to FeedbackDetailScreen with required parameters
|
||||
|
||||
|
||||
navigation.navigate('Patients', {
|
||||
screen: 'FeedbackDetail',
|
||||
params: {
|
||||
navigation.navigate('FeedbackDetail',
|
||||
{
|
||||
patientId: prediction.patid,
|
||||
patientName: prediction.patientdetails.Name || 'Unknown Patient',
|
||||
seriesNumber: prediction.prediction.processing_info.filename || 'Unknown Series',
|
||||
@ -685,7 +684,7 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
|
||||
console.log('Feedback submitted, refreshing dashboard...');
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
console.log('Navigation successful to FeedbackDetailScreen');
|
||||
} catch (error) {
|
||||
|
||||
@ -38,7 +38,7 @@ export const shadows = {
|
||||
},
|
||||
primary: {
|
||||
shadowColor: colors.primary,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
elevation: 6,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user