ERROR  TypeError: Cannot read property 'join' of undefined

This error is located at:
    in ExercisesScreen (created by SceneView)

// screens/ExercisesScreen.js

import React, { useEffect, useState, useContext, useCallback } from 'react';
import {
    View,
    Text,
    FlatList,
    Image,
    StyleSheet,
    ActivityIndicator,
    Alert,
    TouchableOpacity,
    Linking,
    TextInput,
    RefreshControl,
    ScrollView,
    TouchableWithoutFeedback,
} from 'react-native';
import axios from 'axios';
import { AuthContext } from '../context/AuthContext';
import debounce from 'lodash.debounce';
import * as SecureStore from 'expo-secure-store';
import { useTheme, Button, Portal } from 'react-native-paper';
import { Picker } from '@react-native-picker/picker';
import CustomMultiSelect from '../Components/CustomMultiSelect'; // Import your CustomMultiSelect component

const ExercisesScreen = () => {
    const { authToken } = useContext(AuthContext);
    const [exercises, setExercises] = useState([]);
    const [loading, setLoading] = useState(true);
    const [searchQuery, setSearchQuery] = useState('');
    const [isRefreshing, setIsRefreshing] = useState(false);

    const theme = useTheme(); // Accessing the theme

    // Filter state
    const [filterModalVisible, setFilterModalVisible] = useState(false);
    const [selectedMuscles, setSelectedMuscles] = useState([]);
    const [selectedEquipment, setSelectedEquipment] = useState([]);
    const [selectedDifficulty, setSelectedDifficulty] = useState('');

    // Selected exercise for detail modal
    const [selectedExercise, setSelectedExercise] = useState(null);
    const [detailModalVisible, setDetailModalVisible] = useState(false);

    // Define the cache keys
    const CACHE_KEY = 'cachedExercises';
    const CACHE_TIMESTAMP_KEY = 'cachedExercisesTimestamp';
    const MAX_CACHE_AGE = 24 * 60 * 60 * 1000; // 24 hours in milliseconds

    // Predefined options for filters
    const muscleOptions = [
        { id: 'chest', name: 'Chest' },
        { id: 'back', name: 'Back' },
        { id: 'legs', name: 'Legs' },
        { id: 'arms', name: 'Arms' },
        { id: 'shoulders', name: 'Shoulders' },
        // Add more muscles as needed
    ];

    const equipmentOptions = [
        { id: 'barbell', name: 'Barbell' },
        { id: 'dumbbell', name: 'Dumbbell' },
        { id: 'kettlebell', name: 'Kettlebell' },
        { id: 'machine', name: 'Machine' },
        { id: 'bodyweight', name: 'Bodyweight' },
        { id: 'band', name: 'Band' },
        { id: 'plate', name: 'Plate' },
        { id: 'other', name: 'Other' },
        // Add more equipment as needed
    ];

    const difficultyOptions = [
        { label: 'Beginner', value: 'beginner' },
        { label: 'Intermediate', value: 'intermediate' },
        { label: 'Advanced', value: 'advanced' },
    ];

    useEffect(() => {
        loadCachedExercises();
    }, [authToken]); // Reload when token changes

    // Debounced fetch function to prevent multiple rapid API calls
    const debouncedFetchExercises = useCallback(
        debounce((query, filters) => {
            fetchExercises(query, filters);
        }, 300),
        [authToken]
    );

    useEffect(() => {
        // Trigger debounced search whenever searchQuery or filters change
        if (searchQuery.length === 0 || searchQuery.length > 2) { // Trigger search after 3 characters
            const filters = {
                muscles: selectedMuscles.join(','),
                equipment: selectedEquipment.join(','),
                difficulty: selectedDifficulty,
                // Removed gender from filters
            };
            debouncedFetchExercises(searchQuery, filters);
        }
    }, [searchQuery, selectedMuscles, selectedEquipment, selectedDifficulty, debouncedFetchExercises]);

    /**
     * Load exercises from SecureStore
     */
    const loadCachedExercises = async () => {
        if (!authToken) {
            setLoading(false);
            return;
        }

        try {
            const [cachedData, cachedTimestamp] = await Promise.all([
                SecureStore.getItemAsync(CACHE_KEY),
                SecureStore.getItemAsync(CACHE_TIMESTAMP_KEY),
            ]);

            if (cachedData && cachedTimestamp) {
                const parsedData = JSON.parse(cachedData);
                const timestamp = parseInt(cachedTimestamp, 10);
                const currentTime = Date.now();
                const cacheAge = currentTime - timestamp;

                if (cacheAge < MAX_CACHE_AGE) {
                    setExercises(parsedData);
                    console.log('Loaded exercises from SecureStore.');
                    setLoading(false);
                    return;
                } else {
                    console.log('Cache is stale. Fetching fresh data.');
                }
            } else {
                console.log('No cached data found. Fetching from API.');
            }

            // Fetch fresh data
            fetchExercises();
        } catch (error) {
            console.error('Error loading cached exercises:', error);
            Alert.alert('Error', 'Failed to load cached exercises.');
            fetchExercises();
        }
    };

    /**
     * Fetch exercises from the API with optional search and filters
     * @param {string} query - Search query
     * @param {object} filters - Filter parameters
     */
    const fetchExercises = async (query = '', filters = {}) => {
        if (!authToken) {
            Alert.alert('Authentication Error', 'Please log in to fetch exercises.');
            setLoading(false);
            return;
        }

        console.log('Fetching exercises with token:', authToken);

        try {
            const response = await axios.get('http://10.0.0.203:5000/api/exercises', {
                headers: {
                    Authorization: `Bearer ${authToken}`,
                },
                params: {
                    search: query,
                    muscles: filters.muscles || '',
                    equipment: filters.equipment || '',
                    difficulty: filters.difficulty || '',
                    // Removed gender from API params
                    limit: 50,
                },
            });

            if (response.status === 200) {
                const fetchedExercises = response.data;
                setExercises(fetchedExercises);

                // Cache the fetched data with a timestamp
                await Promise.all([
                    SecureStore.setItemAsync(CACHE_KEY, JSON.stringify(fetchedExercises)),
                    SecureStore.setItemAsync(CACHE_TIMESTAMP_KEY, Date.now().toString()),
                ]);
                console.log('Fetched exercises from API and cached.');
            } else {
                console.error('Unexpected response status:', response.status);
                Alert.alert('Error', 'Unexpected response from the server.');
            }
        } catch (error) {
            console.error('Error fetching exercises:', error.response ? error.response.data : error.message);
            Alert.alert('Error', 'Failed to fetch exercises from the server.');
        } finally {
            setLoading(false);
            setIsRefreshing(false);
        }
    };

    /**
     * Handle user input in the search bar
     * @param {string} text - Search query text
     */
    const handleSearch = (text) => {
        setSearchQuery(text);
    };

    /**
     * Handle pull-to-refresh action
     */
    const onRefresh = () => {
        setIsRefreshing(true);
        fetchExercises(searchQuery, {
            muscles: selectedMuscles.join(','),
            equipment: selectedEquipment.join(','),
            difficulty: selectedDifficulty,
            // Removed gender from filters
        });
    };

    /**
     * Render each exercise item
     * @param {object} item - Exercise object
     */
    const renderItem = ({ item }) => (
        <TouchableOpacity onPress={() => openDetailModal(item)}>
            <View style={[styles.itemContainer, { borderColor: theme.colors.border }]}>
                {item.imageUrl ? (
                    <Image source={{ uri: item.imageUrl }} style={styles.image} resizeMode="cover" />
                ) : (
                    <View style={[styles.image, styles.imagePlaceholder, { backgroundColor: theme.colors.surface }]}>
                        <Text style={[styles.imagePlaceholderText, { color: theme.colors.textSecondary }]}>No Image</Text>
                    </View>
                )}
                <Text style={[styles.title, { color: theme.colors.text }]}>
                    {item.name}
                </Text>
                {item.description ? <Text style={[styles.description, { color: theme.colors.textSecondary }]}>{item.description}</Text> : null}
                {item.videoUrl ? (
                    <TouchableOpacity onPress={() => handleOpenURL(item.videoUrl)}>
                        <Text style={[styles.link, { color: theme.colors.primary }]}>Video Tutorial</Text>
                    </TouchableOpacity>
                ) : null}
            </View>
        </TouchableOpacity>
    );

    /**
     * Open the detail modal with exercise details
     * @param {object} exercise - Selected exercise
     */
    const openDetailModal = (exercise) => {
        console.log('Selected Exercise:', exercise); // Debugging line
        setSelectedExercise(exercise);
        setDetailModalVisible(true);
    };

    /**
     * Handle opening URLs with validation
     * @param {string} url - URL to open
     */
    const handleOpenURL = async (url) => {
        const supported = await Linking.canOpenURL(url);
        if (supported) {
            await Linking.openURL(url);
        } else {
            Alert.alert('Invalid URL', 'Cannot open the requested URL.');
        }
    };

    /**
     * Render the filter modal using custom transparent backdrop
     */
    const renderFilterModal = () => {
        if (!filterModalVisible) return null; // Only render if visible

        return (
            <Portal>
                <TouchableWithoutFeedback onPress={() => setFilterModalVisible(false)}>
                    <View style={styles.backdrop}>
                        <TouchableWithoutFeedback>
                            <View style={[styles.modalContent]}>
                                <ScrollView>
                                    <Text style={[styles.modalTitle, { color: theme.colors.text }]}>Filter Exercises</Text>

                                    {/* Muscle Filter */}
                                    <CustomMultiSelect
                                        label="Muscles"
                                        items={muscleOptions}
                                        selectedItems={selectedMuscles}
                                        onSelectedItemsChange={setSelectedMuscles}
                                        placeholder="Select Muscles"
                                    />

                                    {/* Equipment Filter */}
                                    <CustomMultiSelect
                                        label="Equipment"
                                        items={equipmentOptions}
                                        selectedItems={selectedEquipment}
                                        onSelectedItemsChange={setSelectedEquipment}
                                        placeholder="Select Equipment"
                                    />

                                    {/* Difficulty Filter */}
                                    <Text style={[styles.modalLabel, { color: theme.colors.text }]}>Difficulty</Text>
                                    <View style={[styles.pickerContainer, { borderColor: theme.colors.border }]}>
                                        <Picker
                                            selectedValue={selectedDifficulty}
                                            onValueChange={(itemValue) => setSelectedDifficulty(itemValue)}
                                            mode="dropdown"
                                            style={{ color: theme.colors.text }}
                                        >
                                            <Picker.Item label="Select Difficulty" value="" />
                                            {difficultyOptions.map((option) => (
                                                <Picker.Item key={option.value} label={option.label} value={option.value} />
                                            ))}
                                        </Picker>
                                    </View>

                                    {/* Apply and Reset Buttons */}
                                    <View style={styles.modalButtons}>
                                        <Button 
                                            mode="contained" 
                                            onPress={() => setFilterModalVisible(false)} 
                                            style={styles.modalButton}
                                            uppercase={false}
                                        >
                                            Apply
                                        </Button>
                                        <Button
                                            mode="outlined"
                                            onPress={() => {
                                                setSelectedMuscles([]);
                                                setSelectedEquipment([]);
                                                setSelectedDifficulty('');
                                            }}
                                            style={styles.modalButton}
                                            uppercase={false}
                                        >
                                            Reset
                                        </Button>
                                    </View>
                                </ScrollView>
                            </View>
                        </TouchableWithoutFeedback>
                    </View>
                </TouchableWithoutFeedback>
            </Portal>
        );
    };

    /**
     * Render the detail modal for a selected exercise with transparent backdrop
     */
    const renderDetailModal = () => {
        if (!detailModalVisible || !selectedExercise) return null; // Only render if visible and exercise is selected

        return (
            <Portal>
                <TouchableWithoutFeedback onPress={() => setDetailModalVisible(false)}>
                    <View style={styles.backdrop}>
                        <TouchableWithoutFeedback>
                            <View style={[styles.modalContent]}>
                                <ScrollView>
                                    <Text style={[styles.modalTitle, { color: theme.colors.text }]}>{selectedExercise.name}</Text>
                                    {selectedExercise.imageUrl ? (
                                        <Image source={{ uri: selectedExercise.imageUrl }} style={styles.detailImage} resizeMode="cover" />
                                    ) : (
                                        <View style={[styles.image, styles.imagePlaceholder, { backgroundColor: theme.colors.surface }]}>
                                            <Text style={[styles.imagePlaceholderText, { color: theme.colors.textSecondary }]}>No Image</Text>
                                        </View>
                                    )}
                                    {selectedExercise.description ? (
                                        <Text style={[styles.detailDescription, { color: theme.colors.textSecondary }]}>
                                            {selectedExercise.description}
                                        </Text>
                                    ) : null}

                                    <Text style={[styles.detailLabel, { color: theme.colors.text }]}>Muscles:</Text>
                                    <Text style={[styles.detailText, { color: theme.colors.text }]}>{selectedExercise.muscles.join(', ')}</Text>

                                    <Text style={[styles.detailLabel, { color: theme.colors.text }]}>Equipment:</Text>
                                    <Text style={[styles.detailText, { color: theme.colors.text }]}>{selectedExercise.equipment.join(', ')}</Text>

                                    <Text style={[styles.detailLabel, { color: theme.colors.text }]}>Difficulty:</Text>
                                    <Text style={[styles.detailText, { color: theme.colors.text }]}>{capitalizeFirstLetter(selectedExercise.difficulty)}</Text>

                                    {/* Gender Buttons */}
                                    <View style={styles.genderButtons}>
                                        {selectedExercise.gender?.maleUrl ? (
                                            <Button
                                                mode="contained"
                                                onPress={() => handleOpenURL(selectedExercise.gender.maleUrl)}
                                                style={styles.genderButton}
                                                uppercase={false}
                                            >
                                                Male
                                            </Button>
                                        ) : null}
                                        {selectedExercise.gender?.femaleUrl ? (
                                            <Button
                                                mode="contained"
                                                onPress={() => handleOpenURL(selectedExercise.gender.femaleUrl)}
                                                style={styles.genderButton}
                                                uppercase={false}
                                            >
                                                Female
                                            </Button>
                                        ) : null}
                                    </View>

                                    <Button 
                                        mode="outlined" 
                                        onPress={() => setDetailModalVisible(false)} 
                                        style={styles.closeButton}
                                        uppercase={false}
                                    >
                                        Close
                                    </Button>
                                </ScrollView>
                            </View>
                        </TouchableWithoutFeedback>
                    </View>
                </TouchableWithoutFeedback>
            </Portal>
        );
    };

    /**
     * Capitalize the first letter of a string
     * @param {string} string - Input string
     * @returns {string} - Capitalized string
     */
    const capitalizeFirstLetter = (string) => {
        if (!string) return '';
        return string.charAt(0).toUpperCase() + string.slice(1);
    };

    if (loading && exercises.length === 0) {
        return (
            <View style={[styles.loader, { backgroundColor: theme.colors.background }]}>
                <ActivityIndicator size="large" color={theme.colors.primary} />
            </View>
        );
    }

    return (
        <View style={[styles.container, { backgroundColor: theme.colors.background }]}>
            {/* Filter Modal */}
            {filterModalVisible && renderFilterModal()}

            {/* Detail Modal */}
            {renderDetailModal()}

            <View style={styles.header}>
                <TextInput
                    style={[
                        styles.searchBar,
                        {
                            backgroundColor: theme.colors.surface,
                            color: theme.colors.text,
                            borderColor: theme.colors.border,
                        },
                    ]}
                    placeholder="Search exercises..."
                    placeholderTextColor={theme.colors.placeholder}
                    value={searchQuery}
                    onChangeText={handleSearch}
                    autoCapitalize="none"
                    autoCorrect={false}
                    clearButtonMode="while-editing"
                />
                <TouchableOpacity
                    style={[
                        styles.filterButton,
                        { backgroundColor: theme.colors.primary, opacity: 0.9 },
                    ]}
                    onPress={() => setFilterModalVisible(true)}
                >
                    <Text style={[styles.filterButtonText, { color: theme.colors.onSurface }]}>Filter</Text>
                </TouchableOpacity>
            </View>
            <FlatList
                data={exercises}
                keyExtractor={(item, index) => (item._id ? item._id : index.toString())} // Fallback to index if _id is missing
                renderItem={renderItem}
                contentContainerStyle={styles.list}
                keyboardShouldPersistTaps="handled"
                refreshControl={
                    <RefreshControl
                        refreshing={isRefreshing}
                        onRefresh={onRefresh}
                        tintColor={theme.colors.primary}
                        colors={[theme.colors.primary, theme.colors.accent]}
                        progressBackgroundColor={theme.colors.surface}
                    />
                }
                ListEmptyComponent={
                    !loading && (
                        <View style={styles.emptyContainer}>
                            <Text style={[styles.emptyText, { color: theme.colors.textSecondary }]}>No exercises found.</Text>
                        </View>
                    )
                }
            />
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    header: {
        flexDirection: 'row',
        alignItems: 'center',
        paddingHorizontal: 10,
        paddingTop: 10,
        paddingBottom: 5,
    },
    searchBar: {
        flex: 1,
        height: 50,
        borderWidth: 1,
        borderRadius: 8,
        marginRight: 10,
        paddingHorizontal: 15,
        fontSize: 16,
    },
    filterButton: {
        height: 50,
        borderRadius: 8,
        justifyContent: 'center',
        alignItems: 'center',
        paddingHorizontal: 15,
    },
    filterButtonText: {
        fontSize: 14,
        fontWeight: 'bold',
    },
    itemContainer: {
        padding: 10,
        borderBottomWidth: 1,
    },
    image: {
        width: '100%',
        height: 200,
        marginBottom: 10,
        borderRadius: 10,
    },
    imagePlaceholder: {
        justifyContent: 'center',
        alignItems: 'center',
    },
    imagePlaceholderText: {
        fontSize: 16,
    },
    title: {
        fontSize: 18,
        fontWeight: 'bold',
    },
    description: {
        marginTop: 5,
        fontSize: 14,
    },
    link: {
        marginTop: 5,
        textDecorationLine: 'underline',
    },
    loader: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    list: {
        paddingBottom: 20,
    },
    emptyContainer: {
        flex: 1,
        alignItems: 'center',
        marginTop: 50,
    },
    emptyText: {
        fontSize: 16,
    },
    backdrop: {
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: 'rgba(0, 0, 0, 0.3)', // Semi-transparent backdrop for better UX
        justifyContent: 'center',
        alignItems: 'center',
    },
    modalContent: {
        backgroundColor: 'rgba(255, 255, 255, 0.95)', // Slightly transparent to see underlying content
        borderRadius: 10,
        padding: 20,
        maxHeight: '80%',
        width: '90%', // Adjust as needed
        // Optional: Add shadow or elevation for depth
        shadowColor: "#000",
        shadowOffset: {
            width: 0,
            height: 2,
        },
        shadowOpacity: 0.25,
        shadowRadius: 4,
        elevation: 5,
    },
    modalTitle: {
        fontSize: 20,
        fontWeight: 'bold',
        marginBottom: 10,
        textAlign: 'center',
        color: '#000', // Ensure text is visible
    },
    modalLabel: {
        fontSize: 16,
        marginTop: 10,
    },
    pickerContainer: {
        borderWidth: 1,
        borderRadius: 8,
        marginTop: 5,
        marginBottom: 10,
    },
    modalButtons: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        marginTop: 20,
    },
    modalButton: {
        flex: 1,
        marginHorizontal: 5,
    },
    detailImage: {
        width: '100%',
        height: 200,
        borderRadius: 10,
        marginBottom: 10,
    },
    detailDescription: {
        fontSize: 14,
        marginBottom: 10,
    },
    detailLabel: {
        fontSize: 16,
        fontWeight: 'bold',
        marginTop: 10,
    },
    detailText: {
        fontSize: 14,
        marginBottom: 10,
    },
    genderButtons: {
        flexDirection: 'row',
        justifyContent: 'space-around',
        marginTop: 20,
    },
    genderButton: {
        flex: 1,
        marginHorizontal: 10,
        paddingVertical: 10,
    },
    closeButton: {
        marginTop: 20,
    },
    multiSelect: {
        marginTop: 5,
        marginBottom: 10,
    },
    multiSelectDropdown: {
        borderColor: '#ccc',
    },
});

export default ExercisesScreen;

// components/ExerciseList.js
import React, { useEffect, useState, useContext } from 'react';
import { View, Text, FlatList, Image, StyleSheet, ActivityIndicator, Alert } from 'react-native';
import axios from 'axios';
import { AuthContext } from '../context/AuthContext';

const ExerciseList = () => {
    const { authToken } = useContext(AuthContext);
    const [exercises, setExercises] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        const fetchExercises = async () => {
            try {
                const res = await axios.get('http://10.0.0.203:5000/api/exercises', {
                    headers: {
                        Authorization: authToken
                    }
                });
                setExercises(res.data);
            } catch (err) {
                console.error(err);
                Alert.alert('Error', 'Failed to fetch exercises.');
            } finally {
                setLoading(false);
            }
        };
        fetchExercises();
    }, [authToken]);

    const renderItem = ({ item }) => (
        <View style={styles.itemContainer}>
            {item.imageUrl && <Image source={{ uri: item.imageUrl }} style={styles.image} />}
            <Text style={styles.title}>{item.name}</Text>
            <Text>{item.description}</Text>
            {item.videoUrl && <Text style={styles.link}>Video Tutorial: {item.videoUrl}</Text>}
        </View>
    );

    if (loading) {
        return (
            <View style={styles.loader}>
                <ActivityIndicator size="large" color="#0000ff" />
            </View>
        );
    }

    return (
        <FlatList
            data={exercises}
            keyExtractor={(item) => item._id}
            renderItem={renderItem}
            contentContainerStyle={styles.list}
        />
    );
};

const styles = StyleSheet.create({
    itemContainer: { padding: 10, borderBottomWidth: 1, borderColor: '#ccc' },
    image: { width: '100%', height: 200, marginBottom: 10 },
    title: { fontSize: 18, fontWeight: 'bold' },
    link: { color: 'blue', marginTop: 5 },
    loader: { flex: 1, justifyContent: 'center', alignItems: 'center' },
    list: { paddingBottom: 20 },
});

export default ExerciseList;