import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ActivityIndicator, RefreshControl, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { router, useLocalSearchParams } from 'expo-router'; import { api } from '@/services/api'; import { useToast } from '@/components/ui/Toast'; import { LoadingSpinner } from '@/components/ui/LoadingSpinner'; import { FullScreenError } from '@/components/ui/ErrorMessage'; import { AppColors, BorderRadius, FontSizes, FontWeights, Spacing, Shadows, } from '@/constants/theme'; import type { Beneficiary } from '@/types'; import { hasBeneficiaryDevices } from '@/services/BeneficiaryDetailController'; type EquipmentStatus = 'ordered' | 'shipped' | 'delivered'; interface StatusStep { key: EquipmentStatus | 'placed' | 'preparing'; label: string; icon: keyof typeof Ionicons.glyphMap; } const STATUS_STEPS: StatusStep[] = [ { key: 'placed', label: 'Order placed', icon: 'checkmark-circle' }, { key: 'preparing', label: 'Preparing', icon: 'cube-outline' }, { key: 'shipped', label: 'Shipped', icon: 'airplane-outline' }, { key: 'delivered', label: 'Delivered', icon: 'home-outline' }, ]; const getActiveStepIndex = (status: EquipmentStatus): number => { switch (status) { case 'ordered': return 1; // Preparing stage case 'shipped': return 2; case 'delivered': return 3; default: return 0; } }; const getStatusTitle = (status: EquipmentStatus): string => { switch (status) { case 'ordered': return 'Kit Ordered'; case 'shipped': return 'Kit Shipped'; case 'delivered': return 'Kit Delivered'; default: return 'Order Status'; } }; const getStatusDescription = (status: EquipmentStatus): string => { switch (status) { case 'ordered': return 'Your WellNuo kit is being prepared for shipping'; case 'shipped': return 'Your kit is on its way! You should receive it soon.'; case 'delivered': return 'Your kit has arrived! Time to set it up.'; default: return ''; } }; export default function EquipmentStatusScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const toast = useToast(); const [beneficiary, setBeneficiary] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [error, setError] = useState(null); const [isConfirming, setIsConfirming] = useState(false); const loadBeneficiary = useCallback(async () => { if (!id) return; if (!isRefreshing) { setIsLoading(true); } setError(null); try { const response = await api.getWellNuoBeneficiary(parseInt(id, 10)); if (response.ok && response.data) { setBeneficiary(response.data); // Self-guard: Redirect if user has devices - shouldn't be on this page if (hasBeneficiaryDevices(response.data)) { router.replace(`/(tabs)/beneficiaries/${id}`); return; } // Redirect if no equipment order (status is 'none') const status = response.data.equipmentStatus; if (!status || status === 'none') { router.replace(`/(tabs)/beneficiaries/${id}/purchase`); return; } } else { setError(response.error?.message || 'Failed to load beneficiary'); } } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); } finally { setIsLoading(false); setIsRefreshing(false); } }, [id, isRefreshing]); useEffect(() => { loadBeneficiary(); }, [loadBeneficiary]); const handleRefresh = useCallback(() => { setIsRefreshing(true); loadBeneficiary(); }, [loadBeneficiary]); const handleConfirmReceived = async () => { if (!beneficiary || !id) return; setIsConfirming(true); try { // Call API to mark equipment as delivered/received const response = await api.updateBeneficiaryEquipmentStatus(parseInt(id, 10), 'delivered'); if (response.ok) { toast.success('Kit Received!', 'Now let\'s connect your sensors.'); // Navigate to activation screen router.replace({ pathname: '/(auth)/activate', params: { beneficiaryId: id, lovedOneName: beneficiary.name }, }); } else { toast.error('Error', response.error?.message || 'Failed to update status'); } } catch (error) { toast.error('Error', 'Failed to confirm receipt'); } finally { setIsConfirming(false); } }; const handleGoToActivation = () => { if (!beneficiary || !id) return; router.push({ pathname: '/(auth)/activate', params: { beneficiaryId: id, lovedOneName: beneficiary.name }, }); }; if (isLoading) { return ; } if (error || !beneficiary) { return ( ); } const equipmentStatus = (beneficiary.equipmentStatus as EquipmentStatus) || 'ordered'; const activeStepIndex = getActiveStepIndex(equipmentStatus); const isDelivered = equipmentStatus === 'delivered'; return ( {/* Header */} router.replace('/(tabs)')}> {beneficiary.name} } > {/* Status Icon */} {/* Title */} {getStatusTitle(equipmentStatus)} {getStatusDescription(equipmentStatus)} {/* Progress Steps */} {STATUS_STEPS.map((step, index) => { const isCompleted = index < activeStepIndex; const isActive = index === activeStepIndex; const isPending = index > activeStepIndex; return ( {/* Step indicator */} {isCompleted ? ( ) : ( )} {step.label} {/* Connector line */} {index < STATUS_STEPS.length - 1 && ( )} ); })} {/* Action Button */} {isDelivered ? ( Set Up My Kit ) : ( {isConfirming ? ( ) : ( <> I received my kit )} )} {/* Info Card */} What's in the box? • Motion sensor (PIR) • Door/window sensor • Temperature & humidity sensor • WellNuo Hub • Quick start guide {/* Support Link */} Need help? Contact support ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: Spacing.md, paddingVertical: Spacing.sm, borderBottomWidth: 1, borderBottomColor: AppColors.border, }, backButton: { padding: Spacing.xs, }, headerTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, placeholder: { width: 32, }, content: { flex: 1, }, scrollContent: { padding: Spacing.lg, paddingBottom: Spacing.xxl, }, iconContainer: { width: 120, height: 120, borderRadius: 60, backgroundColor: AppColors.primaryLighter, justifyContent: 'center', alignItems: 'center', alignSelf: 'center', marginBottom: Spacing.lg, }, title: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.textPrimary, textAlign: 'center', marginBottom: Spacing.sm, }, subtitle: { fontSize: FontSizes.base, color: AppColors.textSecondary, textAlign: 'center', marginBottom: Spacing.xl, lineHeight: 22, }, // Progress Steps stepsContainer: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.lg, marginBottom: Spacing.lg, ...Shadows.sm, }, stepWrapper: { marginBottom: 0, }, stepRow: { flexDirection: 'row', alignItems: 'center', gap: Spacing.md, }, stepCircle: { width: 28, height: 28, borderRadius: 14, justifyContent: 'center', alignItems: 'center', borderWidth: 2, borderColor: AppColors.border, backgroundColor: AppColors.surface, }, stepCircleCompleted: { backgroundColor: AppColors.success, borderColor: AppColors.success, }, stepCircleActive: { borderColor: AppColors.primary, backgroundColor: AppColors.surface, }, stepCirclePending: { borderColor: AppColors.border, backgroundColor: AppColors.surface, }, stepDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: AppColors.border, }, stepDotActive: { backgroundColor: AppColors.primary, }, stepLabel: { fontSize: FontSizes.base, color: AppColors.textPrimary, fontWeight: FontWeights.medium, }, stepLabelCompleted: { color: AppColors.success, }, stepLabelActive: { color: AppColors.primary, fontWeight: FontWeights.semibold, }, stepLabelPending: { color: AppColors.textMuted, }, connectorWrapper: { paddingLeft: 13, // Half of circle width (28/2) - half of line width height: 24, justifyContent: 'center', }, connector: { width: 2, height: '100%', backgroundColor: AppColors.border, }, connectorCompleted: { backgroundColor: AppColors.success, }, // Action Section actionSection: { marginBottom: Spacing.lg, }, primaryButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: AppColors.primary, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, gap: Spacing.sm, }, primaryButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, outlineButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: AppColors.surface, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, borderWidth: 1, borderColor: AppColors.primary, gap: Spacing.sm, }, outlineButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.primary, }, buttonDisabled: { opacity: 0.7, }, // Info Card infoCard: { backgroundColor: AppColors.infoLight, borderRadius: BorderRadius.lg, padding: Spacing.md, marginBottom: Spacing.lg, }, infoHeader: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, marginBottom: Spacing.sm, }, infoTitle: { fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, color: AppColors.info, }, infoList: { gap: 4, }, infoItem: { fontSize: FontSizes.sm, color: AppColors.info, lineHeight: 20, }, // Support Link supportLink: { flexDirection: 'row', alignItems: 'center', backgroundColor: AppColors.surface, padding: Spacing.md, borderRadius: BorderRadius.lg, gap: Spacing.md, }, supportText: { flex: 1, fontSize: FontSizes.base, color: AppColors.textSecondary, }, });