// app/(workout)/create-template.tsx import React, { useState } from 'react'; import { View, ScrollView, StyleSheet, Platform, TextInput, TouchableOpacity, Alert, KeyboardAvoidingView } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Feather } from '@expo/vector-icons'; import { useColorScheme } from '@/hooks/useColorScheme'; import { router, useLocalSearchParams } from 'expo-router'; import { useWorkout } from '@/contexts/WorkoutContext'; import { Input } from '@/components/form/Input'; import { Select } from '@/components/form/Select'; import { ThemedText } from '@/components/ThemedText'; import EditableText from '@/components/EditableText'; import { spacing } from '@/styles/sharedStyles'; import { generateId } from '@/utils/ids'; import { BaseExercise } from '@/types/exercise'; import { WorkoutTemplate, TemplateCategory } from '@/types/workout'; import { NostrEventKind } from '@/types/events'; const WORKOUT_TYPES: Array<{ label: string; value: WorkoutTemplate['type'] }> = [ { label: 'Strength', value: 'strength' }, { label: 'Circuit', value: 'circuit' }, { label: 'EMOM', value: 'emom' }, { label: 'AMRAP', value: 'amrap' } ]; const TEMPLATE_CATEGORIES: Array<{ label: string; value: TemplateCategory }> = [ { label: 'Full Body', value: 'Full Body' }, { label: 'Upper/Lower', value: 'Upper/Lower' }, { label: 'Push/Pull/Legs', value: 'Push/Pull/Legs' }, { label: 'Custom', value: 'Custom' } ]; interface CreateTemplateScreenProps { initialExercises?: BaseExercise[]; } function CreateTemplateScreen({ initialExercises = [] }: CreateTemplateScreenProps) { const { colors } = useColorScheme(); const params = useLocalSearchParams(); const { saveTemplate } = useWorkout(); const parsedExercises = params.exercises ? JSON.parse(decodeURIComponent(params.exercises as string)) as BaseExercise[] : initialExercises; // Form state matching Nostr spec const [title, setTitle] = useState('New Template'); const [description, setDescription] = useState(''); const [workoutType, setWorkoutType] = useState('strength'); const [category, setCategory] = useState('Custom'); const [exercises, setExercises] = useState(parsedExercises); const [rounds, setRounds] = useState(''); const [duration, setDuration] = useState(''); const [intervalTime, setIntervalTime] = useState(''); const [restBetweenRounds, setRestBetweenRounds] = useState(''); const [tags, setTags] = useState([]); const [error, setError] = useState(null); const handleWorkoutTypeChange = (value: string | string[]) => { if (typeof value === 'string') { setWorkoutType(value as WorkoutTemplate['type']); } }; const handleCategoryChange = (value: string | string[]) => { if (typeof value === 'string') { setCategory(value as TemplateCategory); } }; const handleSave = async () => { try { if (!title.trim()) { Alert.alert('Error', 'Template must have a title'); return; } if (exercises.length === 0) { Alert.alert('Error', 'Template must include at least one exercise'); return; } // Create template following NIP-XX spec const template: WorkoutTemplate = { id: generateId(), title: title.trim(), type: workoutType, description: description, category: category, exercises: exercises.map(exercise => ({ exercise, targetSets: 0, targetReps: 0, })), tags: tags, rounds: rounds ? parseInt(rounds) : undefined, duration: duration ? parseInt(duration) * 60 : undefined, interval: intervalTime ? parseInt(intervalTime) : undefined, restBetweenRounds: restBetweenRounds ? parseInt(restBetweenRounds) : undefined, isPublic: false, created_at: Date.now(), availability: { source: ['local'] }, notes: '' }; await saveTemplate(template); router.back(); } catch (error) { console.error('Error saving template:', error); setError('Failed to save template. Please try again.'); } }; return ( router.back()} style={styles.backButton}> Save Template {workoutType !== 'strength' && ( Workout Parameters )} Exercises ({exercises.length}) {exercises.map((exercise: BaseExercise, index: number) => ( {exercise.title} { const newExercises = [...exercises]; newExercises.splice(index, 1); setExercises(newExercises); }} > ))} { router.push({ pathname: '/(workout)/add-exercises' as const, params: { mode: 'template' } }); }} > Add Exercise ); } const styles = StyleSheet.create({ container: { flex: 1, }, keyboardAvoidView: { flex: 1, }, header: { paddingHorizontal: spacing.medium, paddingVertical: spacing.small, borderBottomWidth: 1, }, headerRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, backButton: { padding: spacing.small, }, saveButton: { paddingVertical: spacing.small, paddingHorizontal: spacing.medium, borderRadius: 8, }, saveButtonText: { fontWeight: '600', fontSize: 16, }, scrollContainer: { flex: 1, }, scrollContent: { padding: spacing.medium, }, titleSection: { marginBottom: spacing.medium, }, titleContainer: { flex: 1, }, title: { fontSize: 32, fontWeight: 'bold', }, section: { marginBottom: spacing.medium, padding: spacing.medium, borderRadius: 12, gap: spacing.medium, }, sectionTitle: { fontSize: 18, fontWeight: '600', }, exerciseCard: { paddingVertical: spacing.medium, borderBottomWidth: 1, }, exerciseHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, exerciseName: { fontSize: 16, fontWeight: '600', }, addButton: { padding: spacing.medium, borderRadius: 8, alignItems: 'center', marginTop: spacing.small, }, addButtonText: { fontSize: 16, fontWeight: '600', }, }); export default CreateTemplateScreen;