// app/(tabs)/library/templates.tsx import React, { useState } from 'react'; import { View, ScrollView } from 'react-native'; import { router } from 'expo-router'; import { Text } from '@/components/ui/text'; import { Input } from '@/components/ui/input'; import { useFocusEffect } from '@react-navigation/native'; import { Search, Plus, ListFilter } from 'lucide-react-native'; import { FloatingActionButton } from '@/components/shared/FloatingActionButton'; import { NewTemplateSheet } from '@/components/library/NewTemplateSheet'; import { FilterSheet, type FilterOptions, type SourceType } from '@/components/library/FilterSheet'; import { TemplateCard } from '@/components/templates/TemplateCard'; import { ModalTemplateDetails } from '@/components/templates/ModalTemplateDetails'; import { Button } from '@/components/ui/button'; import { Template, TemplateCategory, toWorkoutTemplate } from '@/types/templates'; import { useWorkoutStore } from '@/stores/workoutStore'; import { useTemplateService } from '@/components/DatabaseProvider'; // Default available filters const availableFilters = { equipment: ['Barbell', 'Dumbbell', 'Bodyweight', 'Machine', 'Cables', 'Other'], tags: ['Strength', 'Cardio', 'Mobility', 'Recovery'], source: ['local', 'powr', 'nostr'] as SourceType[] }; // Initial filter state const initialFilters: FilterOptions = { equipment: [], tags: [], source: [] }; // Initial templates - empty array const initialTemplates: Template[] = []; export default function TemplatesScreen() { const templateService = useTemplateService(); // Get the template service const [showNewTemplate, setShowNewTemplate] = useState(false); const [templates, setTemplates] = useState(initialTemplates); const [searchQuery, setSearchQuery] = useState(''); const [filterSheetOpen, setFilterSheetOpen] = useState(false); const [currentFilters, setCurrentFilters] = useState(initialFilters); const [activeFilters, setActiveFilters] = useState(0); const { isActive, isMinimized } = useWorkoutStore(); const shouldShowFAB = !isActive || !isMinimized; const [debugInfo, setDebugInfo] = useState(''); // State for the modal template details const [selectedTemplateId, setSelectedTemplateId] = useState(null); const [showTemplateModal, setShowTemplateModal] = useState(false); const handleDelete = (id: string) => { setTemplates(current => current.filter(t => t.id !== id)); }; const handleTemplatePress = (template: Template) => { // Just open the modal without navigating to a route setSelectedTemplateId(template.id); setShowTemplateModal(true); }; const handleStartWorkout = async (template: Template) => { try { // Convert to WorkoutTemplate format const workoutTemplate = toWorkoutTemplate(template); // Start the workout - use the template ID await useWorkoutStore.getState().startWorkoutFromTemplate(template.id); // Navigate to the active workout screen router.push('/(workout)/create'); } catch (error) { console.error("Error starting workout:", error); } }; const handleFavorite = async (template: Template) => { try { const workoutTemplate = toWorkoutTemplate(template); const isFavorite = useWorkoutStore.getState().checkFavoriteStatus(template.id); if (isFavorite) { await useWorkoutStore.getState().removeFavorite(template.id); } else { await useWorkoutStore.getState().addFavorite(workoutTemplate); } // Update local state to reflect change setTemplates(current => current.map(t => t.id === template.id ? { ...t, isFavorite: !isFavorite } : t ) ); } catch (error) { console.error('Error toggling favorite status:', error); } }; const handleApplyFilters = (filters: FilterOptions) => { setCurrentFilters(filters); const totalFilters = Object.values(filters).reduce( (acc, curr) => acc + curr.length, 0 ); setActiveFilters(totalFilters); }; // Handle modal close const handleModalClose = () => { setShowTemplateModal(false); }; // Handle favorite change from modal const handleModalFavoriteChange = (templateId: string, isFavorite: boolean) => { // Update local state to reflect change setTemplates(current => current.map(t => t.id === templateId ? { ...t, isFavorite } : t ) ); }; const handleDebugDB = async () => { try { // Get all templates directly const allTemplates = await templateService.getAllTemplates(100); let info = "Database Stats:\n"; info += "--------------\n"; info += `Total templates: ${allTemplates.length}\n\n`; // Template list info += "Templates:\n"; allTemplates.forEach(template => { info += `- ${template.title} (${template.id}) [${template.availability?.source[0] || 'unknown'}]\n`; info += ` Exercises: ${template.exercises.length}\n`; }); setDebugInfo(info); console.log(info); } catch (error) { console.error('Debug error:', error); setDebugInfo(`Error: ${String(error)}`); } }; // Load templates when the screen is focused useFocusEffect( React.useCallback(() => { async function loadTemplates() { try { console.log('[TemplateScreen] Loading templates...'); // Load templates from the database const data = await templateService.getAllTemplates(100); console.log(`[TemplateScreen] Loaded ${data.length} templates from database`); // Convert to Template[] format that the screen expects const formattedTemplates: Template[] = []; for (const template of data) { // Get favorite status const isFavorite = useWorkoutStore.getState().checkFavoriteStatus(template.id); // Convert to Template format formattedTemplates.push({ id: template.id, title: template.title, type: template.type, category: template.category, exercises: template.exercises.map(ex => ({ title: ex.exercise.title, targetSets: ex.targetSets || 0, targetReps: ex.targetReps || 0, equipment: ex.exercise.equipment })), tags: template.tags || [], source: template.availability?.source[0] || 'local', isFavorite }); } // Update the templates state setTemplates(formattedTemplates); } catch (error) { console.error('[TemplateScreen] Error loading templates:', error); } } loadTemplates(); return () => {}; }, []) ); const handleAddTemplate = (template: Template) => { setTemplates(prev => [...prev, template]); setShowNewTemplate(false); }; // Filter templates based on search and applied filters const filteredTemplates = templates.filter(template => { // Filter by search query const matchesSearch = !searchQuery || template.title.toLowerCase().includes(searchQuery.toLowerCase()); // Filter by equipment if any selected const matchesEquipment = currentFilters.equipment.length === 0 || (template.exercises && template.exercises.some(ex => currentFilters.equipment.includes(ex.equipment || '') )); // Filter by tags if any selected const matchesTags = currentFilters.tags.length === 0 || (template.tags && template.tags.some(tag => currentFilters.tags.includes(tag) )); // Filter by source if any selected const matchesSource = currentFilters.source.length === 0 || currentFilters.source.includes(template.source as SourceType); return matchesSearch && matchesEquipment && matchesTags && matchesSource; }); // Separate favorites and regular templates const favoriteTemplates = filteredTemplates.filter(t => t.isFavorite); const regularTemplates = filteredTemplates.filter(t => !t.isFavorite); return ( {/* Search bar with filter button */} {/* Filter Sheet */} setFilterSheetOpen(false)} options={currentFilters} onApplyFilters={handleApplyFilters} availableFilters={availableFilters} /> {/* Debug button */} {/* Debug info display */} {debugInfo ? ( {debugInfo} ) : null} {/* Templates list */} {/* Favorites Section */} {favoriteTemplates.length > 0 && ( Favorites {favoriteTemplates.map(template => ( handleTemplatePress(template)} onDelete={handleDelete} onFavorite={() => handleFavorite(template)} onStartWorkout={() => handleStartWorkout(template)} /> ))} )} {/* All Templates Section */} All Templates {regularTemplates.length > 0 ? ( {regularTemplates.map(template => ( handleTemplatePress(template)} onDelete={handleDelete} onFavorite={() => handleFavorite(template)} onStartWorkout={() => handleStartWorkout(template)} /> ))} ) : ( No templates found. {templates.length > 0 ? 'Try changing your filters.' : 'Create a new workout template by clicking the + button.'} )} {/* Add some bottom padding for FAB */} {shouldShowFAB && ( setShowNewTemplate(true)} /> )} {/* Template Details Modal */} {/* New Template Sheet */} setShowNewTemplate(false)} onSubmit={handleAddTemplate} /> ); }