feat: explore tab, React Query data layer, programs, sync, analytics, testing infrastructure

- Replace browse tab with Supabase-connected explore tab with filters
- Add React Query for data fetching with loading states
- Add 3 structured programs with weekly progression
- Add Supabase anonymous auth sync service
- Add PostHog analytics with screen tracking and events
- Add comprehensive test strategy (Vitest + Maestro E2E)
- Add RevenueCat subscription system with DEV simulation
- Add i18n translations for new screens (EN/FR/DE/ES)
- Add data deletion modal, sync consent modal
- Add assessment screen and program routes
- Add GitHub Actions CI workflow
- Update activity store with sync integration
This commit is contained in:
Millian Lamiaux
2026-03-24 12:04:48 +01:00
parent 8703c484e8
commit cd065d07c3
138 changed files with 26819 additions and 1043 deletions

View File

@@ -116,6 +116,7 @@ function ProblemScreen({ onNext }: { onNext: () => void }) {
<View style={styles.bottomAction}>
<Pressable
style={styles.ctaButton}
testID="onboarding-problem-cta"
onPress={() => {
haptics.buttonTap()
onNext()
@@ -179,6 +180,7 @@ function EmpathyScreen({
return (
<Pressable
key={item.id}
testID={`barrier-${item.id}`}
style={[
styles.barrierCard,
selected && styles.barrierCardSelected,
@@ -206,6 +208,7 @@ function EmpathyScreen({
<View style={styles.bottomAction}>
<Pressable
style={[styles.ctaButton, barriers.length === 0 && styles.ctaButtonDisabled]}
testID="onboarding-empathy-continue"
onPress={() => {
if (barriers.length > 0) {
haptics.buttonTap()
@@ -350,6 +353,7 @@ function SolutionScreen({ onNext }: { onNext: () => void }) {
<View style={styles.bottomAction}>
<Pressable
style={styles.ctaButton}
testID="onboarding-solution-cta"
onPress={() => {
haptics.buttonTap()
onNext()
@@ -467,6 +471,7 @@ function WowScreen({ onNext }: { onNext: () => void }) {
<Animated.View style={[styles.bottomAction, { opacity: ctaOpacity }]}>
<Pressable
style={styles.ctaButton}
testID="onboarding-wow-cta"
onPress={() => {
if (ctaReady) {
haptics.buttonTap()
@@ -556,6 +561,7 @@ function PersonalizationScreen({
placeholderTextColor={colors.text.hint}
autoCapitalize="words"
autoCorrect={false}
testID="name-input"
/>
</View>
@@ -568,6 +574,7 @@ function PersonalizationScreen({
{LEVELS.map((item) => (
<Pressable
key={item.value}
testID={`level-${item.value}`}
style={[
styles.segmentButton,
level === item.value && styles.segmentButtonActive,
@@ -598,6 +605,7 @@ function PersonalizationScreen({
{GOALS.map((item) => (
<Pressable
key={item.value}
testID={`goal-${item.value}`}
style={[
styles.segmentButton,
goal === item.value && styles.segmentButtonActive,
@@ -628,6 +636,7 @@ function PersonalizationScreen({
{FREQUENCIES.map((item) => (
<Pressable
key={item.value}
testID={`frequency-${item.value}x`}
style={[
styles.segmentButton,
frequency === item.value && styles.segmentButtonActive,
@@ -658,6 +667,7 @@ function PersonalizationScreen({
<View style={{ marginTop: SPACING[8] }}>
<Pressable
style={[styles.ctaButton, !name.trim() && styles.ctaButtonDisabled]}
testID="onboarding-personalization-continue"
onPress={() => {
if (name.trim()) {
haptics.buttonTap()
@@ -828,6 +838,7 @@ function PaywallScreen({
<View style={styles.pricingCards}>
{/* Annual */}
<Pressable
testID="plan-yearly"
style={[
styles.pricingCard,
selectedPlan === 'premium-yearly' && styles.pricingCardSelected,
@@ -852,6 +863,7 @@ function PaywallScreen({
{/* Monthly */}
<Pressable
testID="plan-monthly"
style={[
styles.pricingCard,
selectedPlan === 'premium-monthly' && styles.pricingCardSelected,
@@ -870,6 +882,7 @@ function PaywallScreen({
{/* CTA */}
<Pressable
style={[styles.trialButton, isPurchasing && styles.ctaButtonDisabled]}
testID="subscribe-button"
onPress={handlePurchase}
disabled={isPurchasing}
>
@@ -886,17 +899,21 @@ function PaywallScreen({
</View>
{/* Restore Purchases */}
<Pressable style={styles.restoreButton} onPress={handleRestore}>
<Pressable style={styles.restoreButton} onPress={handleRestore} testID="restore-purchases">
<StyledText size={14} color={colors.text.hint}>
{t('onboarding.paywall.restorePurchases')}
</StyledText>
</Pressable>
{/* Skip */}
<Pressable style={styles.skipButton} onPress={() => {
track('onboarding_paywall_skipped')
onSkip()
}}>
<Pressable
style={styles.skipButton}
testID="skip-paywall"
onPress={() => {
track('onboarding_paywall_skipped')
onSkip()
}}
>
<StyledText size={14} color={colors.text.hint}>
{t('onboarding.paywall.skipButton')}
</StyledText>
@@ -1241,6 +1258,7 @@ function createStyles(colors: ThemeColors) {
flex: 1,
paddingVertical: SPACING[5],
alignItems: 'center',
justifyContent: 'center',
borderRadius: RADIUS.GLASS_CARD,
...colors.glass.base,
},