2025-02-09 20:38:38 -05:00
|
|
|
// app/(tabs)/library/exercises.tsx
|
2025-02-19 21:39:47 -05:00
|
|
|
import React, { useState } from 'react';
|
|
|
|
import { View, ActivityIndicator } from 'react-native';
|
2025-02-09 20:38:38 -05:00
|
|
|
import { Text } from '@/components/ui/text';
|
2025-02-18 12:15:19 -05:00
|
|
|
import { Button } from '@/components/ui/button';
|
2025-02-19 21:39:47 -05:00
|
|
|
import { Input } from '@/components/ui/input';
|
|
|
|
import { Search, Dumbbell } from 'lucide-react-native';
|
2025-02-09 20:38:38 -05:00
|
|
|
import { FloatingActionButton } from '@/components/shared/FloatingActionButton';
|
|
|
|
import { NewExerciseSheet } from '@/components/library/NewExerciseSheet';
|
2025-02-19 21:39:47 -05:00
|
|
|
import { SimplifiedExerciseList } from '@/components/exercises/SimplifiedExerciseList';
|
|
|
|
import { ExerciseDetails } from '@/components/exercises/ExerciseDetails';
|
|
|
|
import { ExerciseDisplay, ExerciseType, BaseExercise } from '@/types/exercise';
|
2025-02-18 12:15:19 -05:00
|
|
|
import { useExercises } from '@/lib/hooks/useExercises';
|
2025-02-17 11:24:17 -05:00
|
|
|
|
2025-02-09 20:38:38 -05:00
|
|
|
export default function ExercisesScreen() {
|
|
|
|
const [showNewExercise, setShowNewExercise] = useState(false);
|
2025-02-19 21:39:47 -05:00
|
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
|
|
const [activeFilter, setActiveFilter] = useState<ExerciseType | null>(null);
|
|
|
|
const [selectedExercise, setSelectedExercise] = useState<ExerciseDisplay | null>(null);
|
2025-02-09 20:38:38 -05:00
|
|
|
|
2025-02-18 12:15:19 -05:00
|
|
|
const {
|
|
|
|
exercises,
|
|
|
|
loading,
|
|
|
|
error,
|
|
|
|
createExercise,
|
|
|
|
deleteExercise,
|
2025-02-19 21:39:47 -05:00
|
|
|
refreshExercises,
|
|
|
|
updateFilters,
|
|
|
|
clearFilters
|
2025-02-18 12:15:19 -05:00
|
|
|
} = useExercises();
|
2025-02-16 23:53:28 -05:00
|
|
|
|
2025-02-19 21:39:47 -05:00
|
|
|
// Filter exercises based on search query
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (searchQuery) {
|
|
|
|
updateFilters({ searchQuery });
|
|
|
|
} else {
|
|
|
|
updateFilters({ searchQuery: undefined });
|
2025-02-16 23:53:28 -05:00
|
|
|
}
|
2025-02-19 21:39:47 -05:00
|
|
|
}, [searchQuery, updateFilters]);
|
|
|
|
|
|
|
|
// Update type filter when activeFilter changes
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (activeFilter) {
|
|
|
|
updateFilters({ type: [activeFilter] });
|
|
|
|
} else {
|
|
|
|
clearFilters();
|
2025-02-18 12:15:19 -05:00
|
|
|
}
|
2025-02-19 21:39:47 -05:00
|
|
|
}, [activeFilter, updateFilters, clearFilters]);
|
2025-02-18 12:15:19 -05:00
|
|
|
|
2025-02-19 21:39:47 -05:00
|
|
|
const handleExercisePress = (exercise: ExerciseDisplay) => {
|
|
|
|
setSelectedExercise(exercise);
|
2025-02-16 23:53:28 -05:00
|
|
|
};
|
2025-02-09 20:38:38 -05:00
|
|
|
|
2025-02-19 21:39:47 -05:00
|
|
|
const handleEdit = async () => {
|
|
|
|
// TODO: Implement edit functionality
|
|
|
|
setSelectedExercise(null);
|
2025-02-09 20:38:38 -05:00
|
|
|
};
|
|
|
|
|
2025-02-19 21:39:47 -05:00
|
|
|
const handleCreateExercise = async (exerciseData: BaseExercise) => {
|
|
|
|
// Convert BaseExercise to include required source information
|
|
|
|
const exerciseWithSource: Omit<BaseExercise, 'id'> = {
|
|
|
|
...exerciseData,
|
|
|
|
availability: {
|
|
|
|
source: ['local']
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
await createExercise(exerciseWithSource);
|
|
|
|
setShowNewExercise(false);
|
2025-02-09 20:38:38 -05:00
|
|
|
};
|
2025-02-18 12:15:19 -05:00
|
|
|
if (loading) {
|
|
|
|
return (
|
|
|
|
<View className="flex-1 items-center justify-center bg-background">
|
2025-02-19 21:39:47 -05:00
|
|
|
<ActivityIndicator size="large" color="#8B5CF6" />
|
|
|
|
<Text className="mt-4 text-muted-foreground">Loading exercises...</Text>
|
2025-02-18 12:15:19 -05:00
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return (
|
|
|
|
<View className="flex-1 items-center justify-center p-4 bg-background">
|
|
|
|
<Text className="text-destructive text-center mb-4">
|
|
|
|
{error.message}
|
|
|
|
</Text>
|
|
|
|
<Button onPress={refreshExercises}>
|
2025-02-19 21:39:47 -05:00
|
|
|
<Text className="text-primary-foreground">Retry</Text>
|
2025-02-18 12:15:19 -05:00
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-02-09 20:38:38 -05:00
|
|
|
return (
|
|
|
|
<View className="flex-1 bg-background">
|
2025-02-19 21:39:47 -05:00
|
|
|
{/* Search bar */}
|
|
|
|
<View className="px-4 py-3">
|
|
|
|
<View className="relative flex-row items-center bg-muted rounded-xl">
|
|
|
|
<View className="absolute left-3 z-10">
|
|
|
|
<Search size={18} className="text-muted-foreground" />
|
2025-02-18 12:15:19 -05:00
|
|
|
</View>
|
2025-02-19 21:39:47 -05:00
|
|
|
<Input
|
|
|
|
value={searchQuery}
|
|
|
|
onChangeText={setSearchQuery}
|
|
|
|
placeholder="Search"
|
|
|
|
className="pl-9 bg-transparent h-10 flex-1"
|
2025-02-18 12:15:19 -05:00
|
|
|
/>
|
|
|
|
</View>
|
2025-02-19 21:39:47 -05:00
|
|
|
</View>
|
2025-02-18 12:15:19 -05:00
|
|
|
|
2025-02-19 21:39:47 -05:00
|
|
|
{/* Exercises list */}
|
|
|
|
<SimplifiedExerciseList
|
|
|
|
exercises={exercises}
|
|
|
|
onExercisePress={handleExercisePress}
|
|
|
|
/>
|
|
|
|
|
|
|
|
{/* Exercise details sheet */}
|
|
|
|
{selectedExercise && (
|
|
|
|
<ExerciseDetails
|
|
|
|
exercise={selectedExercise}
|
|
|
|
open={!!selectedExercise}
|
|
|
|
onOpenChange={(open) => {
|
|
|
|
if (!open) setSelectedExercise(null);
|
|
|
|
}}
|
|
|
|
onEdit={handleEdit}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{/* FAB for adding new exercise */}
|
2025-02-09 20:38:38 -05:00
|
|
|
<FloatingActionButton
|
|
|
|
icon={Dumbbell}
|
|
|
|
onPress={() => setShowNewExercise(true)}
|
|
|
|
/>
|
|
|
|
|
2025-02-19 21:39:47 -05:00
|
|
|
{/* New exercise sheet */}
|
2025-02-09 20:38:38 -05:00
|
|
|
<NewExerciseSheet
|
|
|
|
isOpen={showNewExercise}
|
|
|
|
onClose={() => setShowNewExercise(false)}
|
2025-02-19 21:39:47 -05:00
|
|
|
onSubmit={handleCreateExercise}
|
2025-02-09 20:38:38 -05:00
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|