navigation isue resolved

This commit is contained in:
yashwin-foxy 2025-08-22 14:57:50 +05:30
parent 41de1bbb25
commit 0a95550230
6 changed files with 108 additions and 56 deletions

View File

@ -126,11 +126,14 @@ export const PredictionCard: React.FC<PredictionCardProps> = ({
{/* Header Section */} {/* Header Section */}
<View style={styles.header}> <View style={styles.header}>
<View style={styles.patientInfo}> <View style={styles.patientInfo}>
<Text style={styles.patientName}> <Text style={styles.patientName} numberOfLines={1}>
{prediction.patientdetails.Name || 'Unknown Patient'} {prediction.patientdetails.Name || 'Unknown Patient'}
</Text> </Text>
<Text style={styles.patientDetails}> <Text style={styles.patientDetails} numberOfLines={1} ellipsizeMode="head">
{prediction.patientdetails.PatID} {prediction.patientdetails.PatAge} {prediction.patientdetails.PatSex} {prediction.patientdetails.PatID}
</Text>
<Text style={styles.patientDetailsSecondary}>
{prediction.patientdetails.PatAge} {prediction.patientdetails.PatSex}
</Text> </Text>
</View> </View>
@ -255,7 +258,12 @@ const styles = StyleSheet.create({
borderRadius: theme.borderRadius.large, borderRadius: theme.borderRadius.large,
padding: theme.spacing.md, padding: theme.spacing.md,
marginBottom: 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: { header: {
@ -278,6 +286,12 @@ const styles = StyleSheet.create({
}, },
patientDetails: { 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, fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular, fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary, color: theme.colors.textSecondary,

View File

@ -5,7 +5,7 @@
* Copyright (c) Spurrin Innovations. All rights reserved. * Copyright (c) Spurrin Innovations. All rights reserved.
*/ */
import React from 'react'; import React, { useMemo, useCallback } from 'react';
import { import {
View, View,
Text, Text,
@ -44,8 +44,14 @@ interface PredictionsListProps {
* - Loading states * - Loading states
* - Error handling * - Error handling
* - Empty states * - 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, onPredictionPress,
}) => { }) => {
const { const {
@ -57,6 +63,14 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
refreshPredictions, refreshPredictions,
} = usePredictions(); } = 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 // UTILITY FUNCTIONS
// ============================================================================ // ============================================================================
@ -66,18 +80,18 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
* *
* Purpose: Switch between radiologist feedback status tabs * 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(tab);
}; }, [switchTab]);
/** /**
* Handle Prediction Press * Handle Prediction Press
* *
* Purpose: Handle when a prediction card is pressed * Purpose: Handle when a prediction card is pressed
*/ */
const handlePredictionPress = (prediction: PredictionData) => { const handlePredictionPress = useCallback((prediction: PredictionData) => {
onPredictionPress(prediction); onPredictionPress(prediction);
}; }, [onPredictionPress]);
/** /**
* Render Tab Button * Render Tab Button
@ -150,15 +164,15 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
// RENDER // RENDER
// ============================================================================ // ============================================================================
// Debug logging to see current state // Performance optimization: Only log when needed (development mode)
console.log('🔍 PredictionsList render debug:'); // Note: Tab switching is now instant due to pre-loaded datasets
console.log('Active tab:', activeTab); if (__DEV__ && currentPredictions.length > 0) {
console.log('Current predictions count:', currentPredictions.length); console.log('🔍 PredictionsList render debug:', {
console.log('Current predictions sample:', currentPredictions.slice(0, 2).map(p => ({ activeTab,
id: p.id, count: currentPredictions.length,
has_feedback: p.has_provided_feedback, performance: 'Instant tab switching - datasets pre-loaded'
feedbacks_count: p.feedbacks?.length || 0 });
}))); }
return ( return (
<View style={styles.container}> <View style={styles.container}>
@ -184,46 +198,48 @@ export const PredictionsList: React.FC<PredictionsListProps> = ({
{currentPredictions.length} prediction{currentPredictions.length !== 1 ? 's' : ''} found {currentPredictions.length} prediction{currentPredictions.length !== 1 ? 's' : ''} found
{activeTab === 'with-feedback' && ( {activeTab === 'with-feedback' && (
<Text style={styles.feedbackCount}> <Text style={styles.feedbackCount}>
{' • '}{currentPredictions.filter(p => p.feedbacks && p.feedbacks.length > 0).length} with feedback {' • '}{feedbackCount} with feedback
</Text> </Text>
)} )}
</Text> </Text>
</View> </View>
)} )}
{/* Horizontal Scrolling Predictions */} {/* Horizontal Scrolling Predictions */}
<FlatList <View style={styles.listWrapper}>
data={currentPredictions} <FlatList
renderItem={({ item }) => ( data={currentPredictions}
<View style={styles.predictionCardWrapper}> renderItem={({ item }) => (
<PredictionCard <View style={styles.predictionCardWrapper}>
prediction={item} <PredictionCard
onPress={() => handlePredictionPress(item)} prediction={item}
/> onPress={() => handlePredictionPress(item)}
</View> />
)} </View>
keyExtractor={(item) => item.id.toString()} )}
contentContainerStyle={styles.listContainer} keyExtractor={(item) => item.id.toString()}
showsHorizontalScrollIndicator={true} contentContainerStyle={styles.listContainer}
showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false}
horizontal={true} showsVerticalScrollIndicator={false}
scrollEnabled={true} horizontal={true}
refreshControl={ scrollEnabled={true}
<RefreshControl refreshControl={
refreshing={currentLoadingState} <RefreshControl
onRefresh={refreshPredictions} refreshing={currentLoadingState}
colors={[theme.colors.primary]} onRefresh={refreshPredictions}
tintColor={theme.colors.primary} colors={[theme.colors.primary]}
/> tintColor={theme.colors.primary}
} />
ListEmptyComponent={renderEmptyState} }
/> ListEmptyComponent={renderEmptyState}
/>
</View>
</> </>
)} )}
</View> </View>
</View> </View>
); );
}; });
// ============================================================================ // ============================================================================
// STYLES // STYLES
@ -271,10 +287,19 @@ const styles = StyleSheet.create({
contentContainer: { contentContainer: {
flex: 1, flex: 1,
minHeight: 0,
},
listWrapper: {
// flex: 1,
minHeight: 0, // Prevent flex overflow
marginBottom: theme.spacing.md, // Add bottom margin for shadow visibility
}, },
listContainer: { listContainer: {
padding: theme.spacing.md, padding: 2,
paddingBottom: theme.spacing.xxl, // Increased bottom padding for shadow visibility
alignItems: 'flex-start', alignItems: 'flex-start',
}, },
@ -366,7 +391,9 @@ const styles = StyleSheet.create({
predictionCardWrapper: { predictionCardWrapper: {
marginRight: theme.spacing.md, marginRight: theme.spacing.md,
marginBottom: theme.spacing.md, // Add bottom margin for shadow visibility
width: 280, // Uniform width for all cards width: 280, // Uniform width for all cards
// Height will be determined by content naturally
}, },
}); });

View File

@ -53,12 +53,14 @@ export const usePredictions = () => {
const activeTab = useAppSelector(selectActiveTab); const activeTab = useAppSelector(selectActiveTab);
const searchQuery = useAppSelector(selectSearchQuery); const searchQuery = useAppSelector(selectSearchQuery);
// Performance optimization: Load both datasets upfront
const predictionsWithFeedback = useAppSelector(selectPredictionsWithFeedback); const predictionsWithFeedback = useAppSelector(selectPredictionsWithFeedback);
const predictionsWithoutFeedback = useAppSelector(selectPredictionsWithoutFeedback); const predictionsWithoutFeedback = useAppSelector(selectPredictionsWithoutFeedback);
const isLoading = useAppSelector(selectIsLoading); const isLoading = useAppSelector(selectIsLoading);
const error = useAppSelector(selectError); const error = useAppSelector(selectError);
// Performance optimization: Direct dataset selection - no filtering needed
const currentPredictions = useAppSelector(selectCurrentPredictions); const currentPredictions = useAppSelector(selectCurrentPredictions);
const currentLoadingState = useAppSelector(selectCurrentLoadingState); const currentLoadingState = useAppSelector(selectCurrentLoadingState);
const currentError = useAppSelector(selectCurrentError); const currentError = useAppSelector(selectCurrentError);
@ -74,6 +76,7 @@ export const usePredictions = () => {
* Switch Active Tab * Switch Active Tab
* *
* Purpose: Change between feedback tabs * Purpose: Change between feedback tabs
* Performance: Instant switching - no filtering needed as both datasets are pre-loaded
*/ */
const switchTab = useCallback((tab: PredictionTabType) => { const switchTab = useCallback((tab: PredictionTabType) => {
dispatch(setActiveTab(tab)); dispatch(setActiveTab(tab));
@ -132,12 +135,14 @@ export const usePredictions = () => {
/** /**
* Auto-fetch data when component mounts or token changes * Auto-fetch data when component mounts or token changes
* Performance optimization: Load both datasets upfront to eliminate tab switching delays
*/ */
useEffect(() => { useEffect(() => {
if (!authToken) return; if (!authToken) return;
// Only fetch if we don't have any predictions yet // Performance optimization: Always fetch if either dataset is empty
if (predictionsWithFeedback.length === 0 && predictionsWithoutFeedback.length === 0) { // This ensures both datasets are loaded upfront for instant tab switching
if (predictionsWithFeedback.length === 0 || predictionsWithoutFeedback.length === 0) {
dispatch(fetchAllPredictions(authToken)); dispatch(fetchAllPredictions(authToken));
} }
}, [authToken, predictionsWithFeedback.length, predictionsWithoutFeedback.length, dispatch]); }, [authToken, predictionsWithFeedback.length, predictionsWithoutFeedback.length, dispatch]);

View File

@ -218,9 +218,16 @@ export const selectError = (state: { predictions: PredictionsState }) => state.p
export const selectCurrentPredictions = (state: { predictions: PredictionsState }) => { export const selectCurrentPredictions = (state: { predictions: PredictionsState }) => {
const { activeTab, predictionsWithFeedback, predictionsWithoutFeedback } = state.predictions; const { activeTab, predictionsWithFeedback, predictionsWithoutFeedback } = state.predictions;
// Performance optimization: Direct dataset selection instead of filtering
return activeTab === 'with-feedback' ? predictionsWithFeedback : predictionsWithoutFeedback; 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 }) => { export const selectCurrentLoadingState = (state: { predictions: PredictionsState }) => {
return state.predictions.isLoading; return state.predictions.isLoading;
}; };

View File

@ -638,9 +638,8 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
// Navigate to FeedbackDetailScreen with required parameters // Navigate to FeedbackDetailScreen with required parameters
navigation.navigate('Patients', { navigation.navigate('FeedbackDetail',
screen: 'FeedbackDetail', {
params: {
patientId: prediction.patid, patientId: prediction.patid,
patientName: prediction.patientdetails.Name || 'Unknown Patient', patientName: prediction.patientdetails.Name || 'Unknown Patient',
seriesNumber: prediction.prediction.processing_info.filename || 'Unknown Series', 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('Feedback submitted, refreshing dashboard...');
} }
} }
}); );
console.log('Navigation successful to FeedbackDetailScreen'); console.log('Navigation successful to FeedbackDetailScreen');
} catch (error) { } catch (error) {

View File

@ -38,7 +38,7 @@ export const shadows = {
}, },
primary: { primary: {
shadowColor: colors.primary, shadowColor: colors.primary,
shadowOffset: { width: 0, height: 4 }, shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3, shadowOpacity: 0.3,
shadowRadius: 8, shadowRadius: 8,
elevation: 6, elevation: 6,