/**
* TabataFit Program Detail Screen
* Shows week progression and workout list
*/
import { View, StyleSheet, ScrollView, Pressable } from 'react-native'
import { useRouter, useLocalSearchParams } from 'expo-router'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { LinearGradient } from 'expo-linear-gradient'
import Ionicons from '@expo/vector-icons/Ionicons'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useHaptics } from '@/src/shared/hooks'
import { useProgramStore } from '@/src/shared/stores'
import { PROGRAMS } from '@/src/shared/data/programs'
import { StyledText } from '@/src/shared/components/StyledText'
import { useThemeColors, BRAND } from '@/src/shared/theme'
import type { ThemeColors } from '@/src/shared/theme/types'
import { SPACING, LAYOUT } from '@/src/shared/constants/spacing'
import { RADIUS } from '@/src/shared/constants/borderRadius'
import type { ProgramId } from '@/src/shared/types'
const FONTS = {
LARGE_TITLE: 28,
TITLE: 24,
TITLE_2: 20,
HEADLINE: 17,
BODY: 16,
CAPTION: 13,
SMALL: 12,
}
export default function ProgramDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>()
const programId = id as ProgramId
const { t } = useTranslation('screens')
const insets = useSafeAreaInsets()
const router = useRouter()
const haptics = useHaptics()
const colors = useThemeColors()
const styles = useMemo(() => createStyles(colors), [colors])
const program = PROGRAMS[programId]
const selectProgram = useProgramStore((s) => s.selectProgram)
const progress = useProgramStore((s) => s.programsProgress[programId])
const isWeekUnlocked = useProgramStore((s) => s.isWeekUnlocked)
const getCurrentWorkout = useProgramStore((s) => s.getCurrentWorkout)
const completion = useProgramStore((s) => s.getProgramCompletion(programId))
if (!program) {
return (
Program not found
)
}
const handleStartProgram = () => {
haptics.buttonTap()
selectProgram(programId)
const currentWorkout = getCurrentWorkout(programId)
if (currentWorkout) {
router.push(`/workout/${currentWorkout.id}`)
}
}
const handleWorkoutPress = (workoutId: string, weekNumber: number) => {
haptics.buttonTap()
router.push(`/workout/${workoutId}`)
}
return (
{/* Header */}
router.back()}>
{program.title}
{/* Program Overview */}
{program.description}
{/* Stats Row */}
{program.durationWeeks}
{t('programs.weeks')}
{program.totalWorkouts}
{t('programs.workouts')}
4
{t('programs.minutes')}
{/* Equipment */}
{t('programs.equipment')}
{program.equipment.required.map((item) => (
{item}
))}
{program.equipment.optional.map((item) => (
{item} {t('programs.optional')}
))}
{/* Focus Areas */}
{t('programs.focusAreas')}
{program.focusAreas.map((area) => (
{area}
))}
{/* Progress Overview */}
{progress.completedWorkoutIds.length > 0 && (
{t('programs.yourProgress')}
{completion}%
{progress.completedWorkoutIds.length} {t('programs.of')} {program.totalWorkouts} {t('programs.workoutsComplete')}
)}
{/* Weeks */}
{t('programs.trainingPlan')}
{program.weeks.map((week) => {
const isUnlocked = isWeekUnlocked(programId, week.weekNumber)
const isCurrentWeek = progress.currentWeek === week.weekNumber
const weekCompletion = week.workouts.filter(w =>
progress.completedWorkoutIds.includes(w.id)
).length
return (
{/* Week Header */}
{week.title}
{!isUnlocked && (
)}
{isCurrentWeek && isUnlocked && (
{t('programs.current')}
)}
{week.description}
{weekCompletion > 0 && (
{weekCompletion}/{week.workouts.length} {t('programs.complete')}
)}
{/* Week Workouts */}
{isUnlocked && (
{week.workouts.map((workout, index) => {
const isCompleted = progress.completedWorkoutIds.includes(workout.id)
const isLocked = !isCompleted && index > 0 &&
!progress.completedWorkoutIds.includes(week.workouts[index - 1].id) &&
week.weekNumber === progress.currentWeek
return (
!isLocked && handleWorkoutPress(workout.id, week.weekNumber)}
disabled={isLocked}
>
{isCompleted ? (
) : isLocked ? (
) : (
{index + 1}
)}
{workout.title}
{workout.exercises.length} {t('programs.exercises')} • {workout.duration} {t('programs.min')}
{!isLocked && !isCompleted && (
)}
)
})}
)}
)
})}
{/* Bottom CTA */}
{progress.completedWorkoutIds.length === 0
? t('programs.startProgram')
: progress.isProgramCompleted
? t('programs.restartProgram')
: t('programs.continueTraining')
}
)
}
function createStyles(colors: ThemeColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.bg.base,
},
scrollView: {
flex: 1,
},
scrollContent: {
paddingHorizontal: LAYOUT.SCREEN_PADDING,
},
// Header
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: LAYOUT.SCREEN_PADDING,
paddingVertical: SPACING[3],
},
backButton: {
width: 40,
height: 40,
alignItems: 'center',
justifyContent: 'center',
},
placeholder: {
width: 40,
},
// Overview
overviewSection: {
marginTop: SPACING[2],
marginBottom: SPACING[6],
},
description: {
marginBottom: SPACING[5],
lineHeight: 24,
},
statsRow: {
flexDirection: 'row',
justifyContent: 'space-around',
marginBottom: SPACING[5],
paddingVertical: SPACING[4],
backgroundColor: colors.bg.surface,
borderRadius: RADIUS.LG,
},
statBox: {
alignItems: 'center',
},
// Equipment
equipmentSection: {
marginBottom: SPACING[4],
},
equipmentList: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: SPACING[2],
marginTop: SPACING[2],
},
equipmentTag: {
backgroundColor: colors.bg.surface,
paddingHorizontal: SPACING[3],
paddingVertical: SPACING[1],
borderRadius: RADIUS.FULL,
},
optionalTag: {
opacity: 0.7,
},
// Focus
focusSection: {
marginBottom: SPACING[4],
},
focusList: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: SPACING[2],
marginTop: SPACING[2],
},
focusTag: {
backgroundColor: `${BRAND.PRIMARY}15`,
paddingHorizontal: SPACING[3],
paddingVertical: SPACING[1],
borderRadius: RADIUS.FULL,
},
// Progress
progressSection: {
backgroundColor: colors.bg.surface,
borderRadius: RADIUS.LG,
padding: SPACING[4],
marginBottom: SPACING[6],
},
progressHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: SPACING[3],
},
progressBarContainer: {
marginBottom: SPACING[2],
},
progressBar: {
height: 8,
borderRadius: 4,
overflow: 'hidden',
},
progressFill: {
height: '100%',
borderRadius: 4,
},
// Weeks
weeksSection: {
marginBottom: SPACING[6],
},
weeksTitle: {
marginBottom: SPACING[4],
},
weekCard: {
backgroundColor: colors.bg.surface,
borderRadius: RADIUS.LG,
marginBottom: SPACING[4],
overflow: 'hidden',
},
weekHeader: {
padding: SPACING[4],
borderBottomWidth: 1,
borderBottomColor: colors.border.glass,
},
weekTitleRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: SPACING[1],
},
currentBadge: {
backgroundColor: BRAND.PRIMARY,
paddingHorizontal: SPACING[2],
paddingVertical: 2,
borderRadius: RADIUS.SM,
},
weekProgress: {
marginTop: SPACING[2],
},
// Workouts List
workoutsList: {
padding: SPACING[2],
},
workoutItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: SPACING[3],
paddingHorizontal: SPACING[3],
borderRadius: RADIUS.MD,
},
workoutCompleted: {
opacity: 0.7,
},
workoutLocked: {
opacity: 0.5,
},
workoutNumber: {
width: 32,
height: 32,
alignItems: 'center',
justifyContent: 'center',
marginRight: SPACING[3],
},
workoutInfo: {
flex: 1,
},
completedText: {
textDecorationLine: 'line-through',
},
// Bottom Bar
bottomBar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: colors.bg.base,
paddingHorizontal: LAYOUT.SCREEN_PADDING,
paddingTop: SPACING[3],
borderTopWidth: 1,
borderTopColor: colors.border.glass,
},
ctaButton: {
borderRadius: RADIUS.LG,
overflow: 'hidden',
},
ctaGradient: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: SPACING[4],
},
ctaIcon: {
marginLeft: SPACING[2],
},
})
}