import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, StyleSheet, FlatList, TouchableOpacity, ActivityIndicator, RefreshControl, Image, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { router } from 'expo-router'; import { useFocusEffect } from '@react-navigation/native'; import { useAuth } from '@/contexts/AuthContext'; import { useBeneficiary } from '@/contexts/BeneficiaryContext'; import { api } from '@/services/api'; import { AppColors, BorderRadius, FontSizes, FontWeights, Spacing, Shadows, AvatarSizes, } from '@/constants/theme'; import type { Beneficiary } from '@/types'; import { isSubscriptionActive } from '@/services/subscription'; // Beneficiary card with equipment status support interface BeneficiaryCardProps { beneficiary: Beneficiary; onPress: () => void; onActivate?: () => void; } // Equipment status config const equipmentStatusConfig = { none: { icon: 'cart-outline' as const, label: 'Get kit', sublabel: 'Order equipment', color: AppColors.textMuted, bgColor: AppColors.backgroundSecondary, }, ordered: { icon: 'cube-outline' as const, label: 'Kit ordered', sublabel: 'Preparing to ship', color: AppColors.info, bgColor: AppColors.infoLight, }, shipped: { icon: 'car-outline' as const, label: 'In transit', sublabel: 'Track your package', color: AppColors.warning, bgColor: AppColors.warningLight, }, delivered: { icon: 'checkmark-circle-outline' as const, label: 'Delivered', sublabel: 'Ready to activate', color: AppColors.success, bgColor: AppColors.successLight, }, active: { icon: 'pulse-outline' as const, label: 'Monitoring', sublabel: 'System active', color: AppColors.success, bgColor: AppColors.successLight, }, }; function BeneficiaryCard({ beneficiary, onPress, onActivate }: BeneficiaryCardProps) { const equipmentStatus = beneficiary.equipmentStatus; const isAwaitingEquipment = equipmentStatus && ['ordered', 'shipped', 'delivered'].includes(equipmentStatus); const isEquipmentActive = equipmentStatus === 'active' || equipmentStatus === 'demo'; const hasSubscription = isSubscriptionActive(beneficiary.subscription); const needsKit = !equipmentStatus || equipmentStatus === 'none'; // Monitoring = equipment works + subscription active (all good!) const isMonitoring = isEquipmentActive && hasSubscription; // No subscription warning: equipment works but no subscription const hasNoSubscription = isEquipmentActive && !hasSubscription; const statusConfig = equipmentStatus ? equipmentStatusConfig[equipmentStatus as keyof typeof equipmentStatusConfig] : equipmentStatusConfig.none; // Use server-provided displayName (customName || originalName) const displayName = beneficiary.displayName; // Check if avatar is valid (not empty, null, or placeholder) const hasValidAvatar = beneficiary.avatar && beneficiary.avatar.trim() !== '' && !beneficiary.avatar.includes('placeholder'); // Handle press - if delivered, go to activate const handlePress = () => { if (equipmentStatus === 'delivered' && onActivate) { onActivate(); } else { onPress(); } }; return ( {/* Avatar */} {hasValidAvatar ? ( ) : ( {displayName.charAt(0).toUpperCase()} )} {/* Name and Status */} {displayName} {/* User's role for this beneficiary */} {beneficiary.role && ( You: {beneficiary.role.charAt(0).toUpperCase() + beneficiary.role.slice(1)} )} {/* Equipment status badge (awaiting delivery) */} {isAwaitingEquipment && statusConfig && ( {statusConfig.label} )} {/* Monitoring badge (equipment works + subscription active) */} {isMonitoring && ( {equipmentStatusConfig.active.label} )} {/* No subscription warning */} {hasNoSubscription && ( No subscription )} {/* Get kit badge (no equipment) */} {needsKit && ( {equipmentStatusConfig.none.label} )} {/* Action button or Arrow */} {equipmentStatus === 'delivered' ? ( Activate ) : ( )} ); } export default function HomeScreen() { const { user } = useAuth(); const { currentBeneficiary, setCurrentBeneficiary } = useBeneficiary(); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [beneficiaries, setBeneficiaries] = useState([]); const [error, setError] = useState(null); // Load beneficiaries when screen is focused (after editing profile, etc.) useFocusEffect( useCallback(() => { loadBeneficiaries(); }, []) ); const loadBeneficiaries = async () => { setIsLoading(true); setError(null); try { const onboardingCompleted = await api.isOnboardingCompleted(); // Get beneficiaries from WellNuo API const response = await api.getAllBeneficiaries(); if (response.ok && response.data) { setBeneficiaries(response.data); if (!currentBeneficiary && response.data.length > 0) { setCurrentBeneficiary(response.data[0]); } } else { if (response.error?.code === 'UNAUTHORIZED') { router.replace('/(auth)/login'); return; } // Show error when API fails const errorMessage = response.error?.message || 'Failed to load beneficiaries'; setError(errorMessage); setBeneficiaries([]); } // Redirect to onboarding if no beneficiaries const allBeneficiaries = response.ok ? (response.data || []) : []; if (allBeneficiaries.length === 0 && !onboardingCompleted) { router.replace({ pathname: '/(auth)/add-loved-one', params: { email: user?.email || '' }, }); return; } } catch (err) { setError('Failed to load beneficiaries'); setBeneficiaries([]); } finally { setIsLoading(false); } }; const handleRefresh = async () => { setIsRefreshing(true); await loadBeneficiaries(); setIsRefreshing(false); }; const handleBeneficiaryPress = (beneficiary: Beneficiary) => { setCurrentBeneficiary(beneficiary); // Always go to beneficiary detail page (which includes MockDashboard) router.push(`/(tabs)/beneficiaries/${beneficiary.id}`); }; const handleActivate = (beneficiary: Beneficiary) => { setCurrentBeneficiary(beneficiary); router.push({ pathname: '/(auth)/activate', params: { lovedOneName: beneficiary.displayName, beneficiaryId: beneficiary.id.toString() }, }); }; const getDisplayName = () => { // Check firstName/lastName from API if (user?.firstName) { return user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName; } // Fallback to email prefix if (user?.email) return user.email.split('@')[0]; return 'User'; }; const displayName = getDisplayName(); const greeting = new Date().getHours() < 12 ? 'Good morning' : new Date().getHours() < 18 ? 'Good afternoon' : 'Good evening'; if (isLoading) { return ( {greeting}, {displayName} Loading... ); } return ( {/* Header */} {greeting}, {displayName} {/* Error Message */} {error && ( {error} Try Again )} {/* Section Title */} {beneficiaries.length > 0 && !error && ( My Loved Ones {beneficiaries.length} )} {/* Content */} {beneficiaries.length === 0 && !error ? ( No loved ones yet Add a loved one to start monitoring their wellbeing and stay connected. router.push('/(auth)/add-loved-one')} > Add Loved One ) : ( <> item.id.toString()} renderItem={({ item }) => ( handleBeneficiaryPress(item)} onActivate={() => handleActivate(item)} /> )} contentContainerStyle={styles.listContent} showsVerticalScrollIndicator={false} refreshControl={ } /> {/* Floating Add Button */} router.push('/(auth)/add-loved-one')} activeOpacity={0.9} > )} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, }, header: { paddingHorizontal: Spacing.lg, paddingTop: Spacing.md, paddingBottom: Spacing.lg, backgroundColor: AppColors.background, }, headerContent: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, greeting: { fontSize: FontSizes.base, color: AppColors.textSecondary, fontWeight: FontWeights.medium, }, displayName: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.textPrimary, marginTop: 2, }, headerAction: { width: 44, height: 44, borderRadius: BorderRadius.lg, backgroundColor: AppColors.primaryLighter, justifyContent: 'center', alignItems: 'center', }, sectionHeader: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: Spacing.lg, marginBottom: Spacing.md, }, sectionTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, sectionCount: { fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, color: AppColors.primary, backgroundColor: AppColors.primaryLighter, paddingHorizontal: Spacing.sm, paddingVertical: 2, borderRadius: BorderRadius.full, marginLeft: Spacing.sm, overflow: 'hidden', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, loadingText: { marginTop: Spacing.md, fontSize: FontSizes.base, color: AppColors.textSecondary, }, errorContainer: { marginHorizontal: Spacing.lg, marginBottom: Spacing.lg, padding: Spacing.lg, backgroundColor: AppColors.errorLight, borderRadius: BorderRadius.lg, borderWidth: 1, borderColor: AppColors.error, }, errorContent: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, }, errorText: { flex: 1, fontSize: FontSizes.base, color: AppColors.error, fontWeight: FontWeights.medium, }, retryButton: { marginTop: Spacing.md, backgroundColor: AppColors.error, paddingVertical: Spacing.sm, paddingHorizontal: Spacing.lg, borderRadius: BorderRadius.md, alignSelf: 'flex-start', }, retryButtonText: { color: AppColors.white, fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, }, emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: Spacing.xl, }, emptyIconContainer: { width: 96, height: 96, borderRadius: BorderRadius['2xl'], backgroundColor: AppColors.primaryLighter, justifyContent: 'center', alignItems: 'center', marginBottom: Spacing.lg, }, emptyTitle: { fontSize: FontSizes.xl, fontWeight: FontWeights.bold, color: AppColors.textPrimary, marginBottom: Spacing.sm, }, emptyText: { fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', lineHeight: 24, marginBottom: Spacing.xl, }, addButtonLarge: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: AppColors.primary, paddingVertical: Spacing.md, paddingHorizontal: Spacing.xl, borderRadius: BorderRadius.lg, gap: Spacing.sm, ...Shadows.primary, }, addButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, listContent: { paddingHorizontal: Spacing.lg, paddingBottom: 100, }, // Card styles card: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, marginBottom: Spacing.md, padding: Spacing.md, flexDirection: 'row', alignItems: 'center', borderWidth: 2, borderColor: 'transparent', ...Shadows.sm, }, cardNoSubscription: { borderColor: AppColors.error, backgroundColor: AppColors.errorLight, }, avatarWrapper: { position: 'relative', }, avatar: { width: AvatarSizes.md, height: AvatarSizes.md, borderRadius: AvatarSizes.md / 2, backgroundColor: AppColors.primaryLighter, justifyContent: 'center', alignItems: 'center', }, avatarImage: { width: AvatarSizes.md, height: AvatarSizes.md, borderRadius: AvatarSizes.md / 2, }, avatarText: { fontSize: FontSizes.xl, fontWeight: FontWeights.bold, color: AppColors.primary, }, avatarNoSubscription: { backgroundColor: AppColors.errorLight, }, avatarTextNoSubscription: { color: AppColors.error, }, info: { flex: 1, marginLeft: Spacing.md, marginRight: Spacing.sm, }, name: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, roleText: { fontSize: FontSizes.xs, color: AppColors.textMuted, marginTop: 2, }, noSubscriptionBadge: { flexDirection: 'row', alignItems: 'center', marginTop: 4, gap: 4, }, noSubscriptionText: { fontSize: FontSizes.xs, fontWeight: FontWeights.medium, color: AppColors.error, }, statusBadge: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: Spacing.sm, paddingVertical: 4, borderRadius: BorderRadius.md, marginTop: 4, alignSelf: 'flex-start', gap: 4, }, statusText: { fontSize: FontSizes.xs, fontWeight: FontWeights.medium, }, activateButton: { backgroundColor: AppColors.primary, paddingHorizontal: Spacing.md, paddingVertical: Spacing.sm, borderRadius: BorderRadius.md, }, activateButtonText: { color: AppColors.white, fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, }, arrowContainer: { width: 32, height: 32, borderRadius: BorderRadius.md, backgroundColor: AppColors.surfaceSecondary, justifyContent: 'center', alignItems: 'center', }, // Floating Action Button fab: { position: 'absolute', bottom: Spacing.xl, right: Spacing.lg, width: 60, height: 60, borderRadius: 30, backgroundColor: AppColors.primary, justifyContent: 'center', alignItems: 'center', ...Shadows.lg, }, });