fix: add missing Workout fields to program workouts and guard against undefined level
Program workouts built by buildProgramWorkouts() were missing level, rounds, calories, and other Workout-interface fields, causing workout.level.toLowerCase() to crash on the detail, collection, and category screens. Added derived defaults (level from week number, category from program id, standard Tabata timings) and defensive fallbacks with ?? 'Beginner' at all call sites. Also fixed a potential division-by-zero when exercises array is empty.
This commit is contained in:
@@ -8,7 +8,7 @@ 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 { Icon } from '@/src/shared/components/Icon'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useHaptics } from '@/src/shared/hooks'
|
||||
@@ -70,7 +70,7 @@ export default function CollectionDetailScreen() {
|
||||
if (!collection) {
|
||||
return (
|
||||
<View style={[styles.container, styles.centered, { paddingTop: insets.top }]}>
|
||||
<Ionicons name="folder-open-outline" size={48} color={colors.text.tertiary} />
|
||||
<Icon name="folder" size={48} color={colors.text.tertiary} />
|
||||
<StyledText size={17} color={colors.text.tertiary} style={{ marginTop: SPACING[3] }}>
|
||||
Collection not found
|
||||
</StyledText>
|
||||
@@ -83,7 +83,7 @@ export default function CollectionDetailScreen() {
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<Pressable testID="collection-back-button" onPress={handleBack} style={styles.backButton}>
|
||||
<Ionicons name="chevron-back" size={24} color={colors.text.primary} />
|
||||
<Icon name="chevron.left" size={24} color={colors.text.primary} />
|
||||
</Pressable>
|
||||
<StyledText size={22} weight="bold" color={colors.text.primary} numberOfLines={1} style={{ flex: 1, textAlign: 'center' }}>
|
||||
{collection.title}
|
||||
@@ -138,7 +138,7 @@ export default function CollectionDetailScreen() {
|
||||
onPress={() => handleWorkoutPress(workout.id)}
|
||||
>
|
||||
<View style={[styles.workoutAvatar, { backgroundColor: BRAND.PRIMARY }]}>
|
||||
<Ionicons name="flame" size={20} color="#FFFFFF" />
|
||||
<Icon name="flame.fill" size={20} color="#FFFFFF" />
|
||||
</View>
|
||||
<View style={styles.workoutInfo}>
|
||||
<StyledText size={17} weight="semibold" color={colors.text.primary}>
|
||||
@@ -147,7 +147,7 @@ export default function CollectionDetailScreen() {
|
||||
<StyledText size={13} color={colors.text.tertiary}>
|
||||
{t('durationLevel', {
|
||||
duration: workout.duration,
|
||||
level: t(`levels.${workout.level.toLowerCase()}`),
|
||||
level: t(`levels.${(workout.level ?? 'Beginner').toLowerCase()}`),
|
||||
})}
|
||||
</StyledText>
|
||||
</View>
|
||||
@@ -155,14 +155,14 @@ export default function CollectionDetailScreen() {
|
||||
<StyledText size={13} color={BRAND.PRIMARY}>
|
||||
{t('units.calUnit', { count: workout.calories })}
|
||||
</StyledText>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.text.tertiary} />
|
||||
<Icon name="chevron.right" size={16} color={colors.text.tertiary} />
|
||||
</View>
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
{workouts.length === 0 && (
|
||||
<View style={styles.emptyState}>
|
||||
<Ionicons name="barbell-outline" size={48} color={colors.text.tertiary} />
|
||||
<Icon name="dumbbell" size={48} color={colors.text.tertiary} />
|
||||
<StyledText size={17} color={colors.text.tertiary} style={{ marginTop: SPACING[3] }}>
|
||||
No workouts in this collection
|
||||
</StyledText>
|
||||
|
||||
Reference in New Issue
Block a user