refactor: code quality cleanup — remove any types, add logger, rename Kine to Tabata

- Phase 0: Rename all Kine references to Tabata (types, files, imports, i18n, analytics events)
- Phase 1: Add test coverage for tabataProgramStore, workoutProgramStore, and color utils (47 tests)
- Phase 2: Remove all `any` types from production code with proper typed replacements
- Phase 3: Replace ~60 raw console.* calls with __DEV__-gated logger utility
- Phase 4: Verify .DS_Store housekeeping (already clean)

0 TypeScript errors, 583/583 tests passing.
This commit is contained in:
Millian Lamiaux
2026-04-17 18:56:24 +02:00
parent e0e02c4550
commit 791f432334
176 changed files with 16508 additions and 2305 deletions

View File

@@ -24,12 +24,16 @@ import { useUserStore } from '@/src/shared/stores'
import { OnboardingStep } from '@/src/shared/components/OnboardingStep'
import { StyledText } from '@/src/shared/components/StyledText'
import { useThemeColors, BRAND, PHASE } from '@/src/shared/theme'
import { useThemeColors } 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 { DURATION, EASE, SPRING } from '@/src/shared/constants/animations'
import { track, identifyUser, setUserProperties, trackScreen } from '@/src/shared/services/analytics'
import { GREEN, NAVY, BORDER_COLORS } from '@/src/shared/constants/colors'
import { withOpacity } from '@/src/shared/utils/color'
import { PHASE } from '@/src/shared/constants/colors'
import { NativeButton } from '@/src/shared/components/native'
import type { FitnessLevel, FitnessGoal, WeeklyFrequency } from '@/src/shared/types'
@@ -85,7 +89,7 @@ function ProblemScreen({ onNext }: { onNext: () => void }) {
marginBottom: SPACING[8],
}}
>
<Icon name="clock.fill" size={80} color={BRAND.PRIMARY} />
<Icon name="clock.fill" size={80} color={GREEN[500]} />
</Animated.View>
<Animated.View style={{ opacity: textOpacity, alignItems: 'center' }}>
@@ -122,7 +126,7 @@ function ProblemScreen({ onNext }: { onNext: () => void }) {
onNext()
}}
>
<StyledText size={17} weight="semibold" color="#FFFFFF">
<StyledText size={17} weight="semibold" color={NAVY[900]}>
{t('onboarding.problem.cta')}
</StyledText>
</Pressable>
@@ -190,7 +194,7 @@ function EmpathyScreen({
<Icon
name={item.icon}
size={28}
color={selected ? BRAND.PRIMARY : colors.text.tertiary}
color={selected ? GREEN[500] : colors.text.tertiary}
/>
<StyledText
size={15}
@@ -219,7 +223,7 @@ function EmpathyScreen({
<StyledText
size={17}
weight="semibold"
color={barriers.length > 0 ? '#FFFFFF' : colors.text.disabled}
color={barriers.length > 0 ? NAVY[900] : colors.text.disabled}
>
{t('common:continue')}
</StyledText>
@@ -280,7 +284,7 @@ function SolutionScreen({ onNext }: { onNext: () => void }) {
<View style={styles.comparisonContainer}>
{/* Tabata bar */}
<View style={styles.barColumn}>
<StyledText size={22} weight="bold" color={BRAND.PRIMARY}>
<StyledText size={22} weight="bold" color={GREEN[500]}>
{t('onboarding.solution.tabataCalories')}
</StyledText>
<View style={styles.barTrack}>
@@ -359,7 +363,7 @@ function SolutionScreen({ onNext }: { onNext: () => void }) {
onNext()
}}
>
<StyledText size={17} weight="semibold" color="#FFFFFF">
<StyledText size={17} weight="semibold" color={NAVY[900]}>
{t('onboarding.solution.cta')}
</StyledText>
</Pressable>
@@ -373,7 +377,7 @@ function SolutionScreen({ onNext }: { onNext: () => void }) {
// ═══════════════════════════════════════════════════════════════════════════
const WOW_FEATURES = [
{ icon: 'timer' as const, iconColor: BRAND.PRIMARY, titleKey: 'onboarding.wow.card1Title', subtitleKey: 'onboarding.wow.card1Subtitle' },
{ icon: 'timer' as const, iconColor: GREEN[500], titleKey: 'onboarding.wow.card1Title', subtitleKey: 'onboarding.wow.card1Subtitle' },
{ icon: 'dumbbell' as const, iconColor: PHASE.REST, titleKey: 'onboarding.wow.card2Title', subtitleKey: 'onboarding.wow.card2Subtitle' },
{ icon: 'mic' as const, iconColor: PHASE.PREP, titleKey: 'onboarding.wow.card3Title', subtitleKey: 'onboarding.wow.card3Subtitle' },
{ icon: 'arrow.up.right' as const, iconColor: PHASE.COMPLETE, titleKey: 'onboarding.wow.card4Title', subtitleKey: 'onboarding.wow.card4Subtitle' },
@@ -452,7 +456,7 @@ function WowScreen({ onNext }: { onNext: () => void }) {
},
]}
>
<View style={[wowStyles.iconCircle, { backgroundColor: `${feature.iconColor}26` }]}>
<View style={[wowStyles.iconCircle, { backgroundColor: withOpacity(feature.iconColor, 0.15) }]}>
<Icon name={feature.icon} size={22} color={feature.iconColor} />
</View>
<View style={wowStyles.textCol}>
@@ -479,7 +483,7 @@ function WowScreen({ onNext }: { onNext: () => void }) {
}
}}
>
<StyledText size={17} weight="semibold" color="#FFFFFF">
<StyledText size={17} weight="semibold" color={NAVY[900]}>
{t('common:next')}
</StyledText>
</Pressable>
@@ -659,7 +663,7 @@ function PersonalizationScreen({
</View>
{name.trim().length > 0 && (
<StyledText size={15} color={BRAND.SUCCESS} style={styles.readyMessage}>
<StyledText size={15} color={GREEN[500]} style={styles.readyMessage}>
{t('onboarding.personalization.readyMessage')}
</StyledText>
)}
@@ -678,7 +682,7 @@ function PersonalizationScreen({
<StyledText
size={17}
weight="semibold"
color={name.trim() ? '#FFFFFF' : colors.text.disabled}
color={name.trim() ? NAVY[900] : colors.text.disabled}
>
{t('common:continue')}
</StyledText>
@@ -822,7 +826,7 @@ function PaywallScreen({
key={featureKey}
style={[styles.featureRow, { opacity: featureAnims[i] }]}
>
<Icon name="checkmark.circle.fill" size={22} color={BRAND.SUCCESS} />
<Icon name="checkmark.circle.fill" size={22} color={GREEN[500]} />
<StyledText
size={16}
color={colors.text.primary}
@@ -846,7 +850,7 @@ function PaywallScreen({
onPress={() => handlePlanSelect('premium-yearly')}
>
<View style={styles.bestValueBadge}>
<StyledText size={11} weight="bold" color="#FFFFFF">
<StyledText size={11} weight="bold" color={NAVY[900]}>
{t('onboarding.paywall.bestValue')}
</StyledText>
</View>
@@ -856,7 +860,7 @@ function PaywallScreen({
<StyledText size={13} color={colors.text.secondary}>
{t('common:units.perYear')}
</StyledText>
<StyledText size={12} weight="semibold" color={BRAND.PRIMARY} style={{ marginTop: SPACING[1] }}>
<StyledText size={12} weight="semibold" color={GREEN[500]} style={{ marginTop: SPACING[1] }}>
{t('onboarding.paywall.savePercent')}
</StyledText>
</Pressable>
@@ -886,7 +890,7 @@ function PaywallScreen({
onPress={handlePurchase}
disabled={isPurchasing}
>
<StyledText size={17} weight="bold" color="#FFFFFF">
<StyledText size={17} weight="bold" color={NAVY[900]}>
{isPurchasing ? '...' : t('onboarding.paywall.trialCta')}
</StyledText>
</Pressable>
@@ -906,8 +910,8 @@ function PaywallScreen({
</Pressable>
{/* Skip */}
<Pressable
style={styles.skipButton}
<Pressable
style={styles.skipButton}
testID="skip-paywall"
onPress={() => {
track('onboarding_paywall_skipped')
@@ -1125,8 +1129,8 @@ function createStyles(colors: ThemeColors) {
// CTA Button
ctaButton: {
height: LAYOUT.BUTTON_HEIGHT,
backgroundColor: BRAND.PRIMARY,
borderRadius: RADIUS.GLASS_BUTTON,
backgroundColor: GREEN[500],
borderRadius: RADIUS.MD,
alignItems: 'center',
justifyContent: 'center',
},
@@ -1146,12 +1150,14 @@ function createStyles(colors: ThemeColors) {
width: (SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2 - SPACING[3]) / 2,
paddingVertical: SPACING[6],
alignItems: 'center',
borderRadius: RADIUS.GLASS_CARD,
...colors.glass.base,
borderRadius: RADIUS.LG,
backgroundColor: NAVY[800],
borderWidth: 1,
borderColor: BORDER_COLORS.DIM,
},
barrierCardSelected: {
borderColor: BRAND.PRIMARY,
backgroundColor: 'rgba(255, 107, 53, 0.1)',
borderColor: GREEN.BORDER,
backgroundColor: GREEN.DIM,
},
// ── Screen 3: Comparison ──
@@ -1181,7 +1187,7 @@ function createStyles(colors: ThemeColors) {
borderRadius: RADIUS.SM,
},
barTabata: {
backgroundColor: BRAND.PRIMARY,
backgroundColor: GREEN[500],
},
barCardio: {
backgroundColor: PHASE.REST,
@@ -1222,7 +1228,7 @@ function createStyles(colors: ThemeColors) {
color: colors.text.primary,
fontSize: 17,
borderWidth: 1,
borderColor: colors.border.glass,
borderColor: BORDER_COLORS.DIM,
},
segmentRow: {
flexDirection: 'row',
@@ -1268,16 +1274,18 @@ function createStyles(colors: ThemeColors) {
paddingVertical: SPACING[5],
alignItems: 'center',
justifyContent: 'center',
borderRadius: RADIUS.GLASS_CARD,
...colors.glass.base,
borderRadius: RADIUS.LG,
backgroundColor: NAVY[800],
borderWidth: 1,
borderColor: BORDER_COLORS.DIM,
},
pricingCardSelected: {
borderColor: BRAND.PRIMARY,
borderColor: GREEN.BORDER,
borderWidth: 2,
backgroundColor: 'rgba(255, 107, 53, 0.08)',
backgroundColor: GREEN.DIM,
},
bestValueBadge: {
backgroundColor: BRAND.PRIMARY,
backgroundColor: GREEN[500],
paddingHorizontal: SPACING[3],
paddingVertical: SPACING[1],
borderRadius: RADIUS.SM,
@@ -1285,8 +1293,8 @@ function createStyles(colors: ThemeColors) {
},
trialButton: {
height: LAYOUT.BUTTON_HEIGHT,
backgroundColor: BRAND.PRIMARY,
borderRadius: RADIUS.GLASS_BUTTON,
backgroundColor: GREEN[500],
borderRadius: RADIUS.MD,
alignItems: 'center',
justifyContent: 'center',
marginTop: SPACING[6],
@@ -1322,7 +1330,7 @@ function createWowStyles(colors: ThemeColors) {
iconCircle: {
width: 44,
height: 44,
borderRadius: 22,
borderRadius: RADIUS.FULL,
alignItems: 'center',
justifyContent: 'center',
},