/** * Tabata Kine Program Detail Screen * Displays program overview, weeks, sessions, and progression for kiné programs */ import React from 'react' import { View, Text, StyleSheet, ScrollView, Pressable } from 'react-native' import { Stack, useRouter, useLocalSearchParams } from 'expo-router' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { Icon } from '@/src/shared/components/Icon' import { useKineProgramStore } from '@/src/shared/stores/kineProgramStore' import { getKineProgramById, getKineSessionsByWeek } from '@/src/shared/data/kine' import { canAccessProgram } from '@/src/shared/services/access' import { useUserStore } from '@/src/shared/stores/userStore' import type { KineProgramId } from '@/src/shared/types/program' import { TYPOGRAPHY } from '@/src/shared/constants/typography' import { SPACING } from '@/src/shared/constants/spacing' import { RADIUS } from '@/src/shared/constants/borderRadius' import { TEXT, NAVY, GREEN, BORDER_COLORS, AMBER, DARK } from '@/src/shared/constants/colors' import { withOpacity } from '@/src/shared/utils/color' export default function KineProgramDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>() const router = useRouter() const insets = useSafeAreaInsets() const programId = id as KineProgramId const program = getKineProgramById(programId) const selectProgram = useKineProgramStore(s => s.selectProgram) const progress = useKineProgramStore(s => s.programsProgress[programId]) const isWeekUnlocked = useKineProgramStore(s => s.isWeekUnlocked) const getCurrentSession = useKineProgramStore(s => s.getCurrentSession) const completion = useKineProgramStore(s => s.getProgramCompletion(programId)) const getProgramStatus = useKineProgramStore(s => s.getProgramStatus) const isPremium = useUserStore(s => s.profile.subscription) !== 'free' const canAccess = canAccessProgram(programId, isPremium) const status = getProgramStatus(programId) if (!program) { return ( Programme non trouvé ) } const handleStartProgram = () => { selectProgram(programId) const session = getCurrentSession(programId) if (session) { router.push(`/workout/${session.id}`) } } const handleSessionPress = (sessionId: string) => { router.push(`/workout/${sessionId}`) } return ( {/* Program header */} {program.title} {program.description} {/* Tier badge */} {program.tier === 'free' ? 'GRATUIT' : 'PREMIUM'} {/* Stats row */} {program.durationWeeks} Semaines {program.sessionsPerWeek} Séances/sem {program.totalSessions} Séances {/* Progress */} {status === 'in-progress' && ( {completion}% complété )} {/* Principles */} {program.principles.length > 0 && ( Principes {program.principles.map((p, i) => ( {p} ))} )} {/* Weeks */} {program.weeks.map(week => { const unlocked = isWeekUnlocked(programId, week.weekNumber) return ( Semaine {week.weekNumber}: {week.title} {week.isDeload && ( Décharge )} {!unlocked && ( )} {week.focus} {/* Sessions */} {week.sessions.map(session => { const isCompleted = progress?.completedSessionIds.includes(session.id) ?? false const sessionLocked = !unlocked return ( !sessionLocked && canAccess && handleSessionPress(session.id)} disabled={sessionLocked || !canAccess} > Séance {session.order}: {session.title} {session.blocks.length} bloc{session.blocks.length > 1 ? 's' : ''} · {session.totalDuration} min · {session.calories} cal {isCompleted && } {!canAccess && !sessionLocked && } ) })} ) })} {/* Completion criteria */} {program.completionCriteria.length > 0 && ( Critères de passage {program.completionCriteria.map((c, i) => ( {c} ))} )} {/* CTA */} {canAccess ? ( {status === 'in-progress' ? 'Continuer le programme' : status === 'completed' ? 'Recommencer' : 'Commencer le programme'} ) : ( router.push('/paywall')}> Débloquer avec Premium )} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: NAVY[900] }, scrollView: { flex: 1 }, heroSection: { padding: SPACING[6], alignItems: 'center' }, iconCircle: { width: 64, height: 64, borderRadius: 32, alignItems: 'center', justifyContent: 'center', marginBottom: SPACING[3] }, programTitle: { ...TYPOGRAPHY.LARGE_TITLE, color: TEXT.PRIMARY, textAlign: 'center' }, programDescription: { ...TYPOGRAPHY.BODY, color: TEXT.SECONDARY, textAlign: 'center', marginTop: SPACING[2], lineHeight: 22 }, tierBadge: { marginTop: SPACING[3], paddingHorizontal: SPACING[2], paddingVertical: 3, borderRadius: RADIUS.SM, borderWidth: 1 }, tierBadgeText: { ...TYPOGRAPHY.LABEL }, statsRow: { flexDirection: 'row', marginTop: SPACING[6], gap: SPACING[8] }, statItem: { alignItems: 'center' }, statValue: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY }, statLabel: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: 2 }, progressSection: { marginTop: SPACING[4], width: '100%' }, progressBar: { height: 4, borderRadius: RADIUS.PILL, backgroundColor: BORDER_COLORS.DIM, overflow: 'hidden' }, progressFill: { height: '100%', borderRadius: RADIUS.PILL }, progressText: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: SPACING[1], textAlign: 'center' }, section: { paddingHorizontal: SPACING[5], marginTop: SPACING[6] }, sectionTitle: { ...TYPOGRAPHY.HEADLINE, color: TEXT.PRIMARY, marginBottom: SPACING[3] }, principleItem: { flexDirection: 'row', gap: SPACING[2], marginBottom: SPACING[2] }, principleBullet: { color: TEXT.TERTIARY, fontSize: 14 }, principleText: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.SECONDARY, flex: 1, lineHeight: 20 }, weekSection: { paddingHorizontal: SPACING[5], marginTop: SPACING[6] }, weekHeader: { flexDirection: 'row', alignItems: 'center', gap: SPACING[2] }, weekTitle: { ...TYPOGRAPHY.TITLE_3, color: TEXT.PRIMARY, flex: 1 }, weekFocus: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: SPACING[1], marginBottom: SPACING[3] }, deloadBadge: { backgroundColor: withOpacity(AMBER[500], 0.2), paddingHorizontal: SPACING[2], paddingVertical: 2, borderRadius: RADIUS.SM }, deloadText: { ...TYPOGRAPHY.LABEL, color: AMBER[500] }, sessionCard: { backgroundColor: NAVY[800], borderRadius: RADIUS.MD, padding: SPACING[3], marginBottom: SPACING[2], borderWidth: 1, borderColor: BORDER_COLORS.DIM }, sessionCardLocked: { opacity: 0.5 }, sessionInfo: { flexDirection: 'row', alignItems: 'center', gap: SPACING[3] }, sessionDot: { width: 8, height: 8, borderRadius: 4 }, sessionTitle: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.PRIMARY }, sessionMeta: { ...TYPOGRAPHY.CAPTION_2, color: TEXT.TERTIARY, marginTop: 2 }, ctaContainer: { position: 'absolute', bottom: 0, left: 0, right: 0, paddingHorizontal: SPACING[5], paddingTop: SPACING[3], backgroundColor: DARK.SCRIM, borderTopWidth: 1, borderTopColor: BORDER_COLORS.DIM }, ctaButton: { height: 52, borderRadius: RADIUS.MD, alignItems: 'center', justifyContent: 'center' }, ctaText: { ...TYPOGRAPHY.BUTTON_MEDIUM, color: NAVY[900], letterSpacing: 0.5 }, errorText: { color: TEXT.SECONDARY, textAlign: 'center', marginTop: 100 }, })