Update activate, purchase and dashboard screens
This commit is contained in:
parent
b869e9e3ab
commit
7186f29f35
@ -18,8 +18,8 @@ import { api } from '@/services/api';
|
|||||||
|
|
||||||
|
|
||||||
export default function ActivateScreen() {
|
export default function ActivateScreen() {
|
||||||
// Get lovedOneName from purchase flow
|
// Get params - lovedOneName from purchase flow, beneficiaryId from existing beneficiary
|
||||||
const params = useLocalSearchParams<{ lovedOneName?: string }>();
|
const params = useLocalSearchParams<{ lovedOneName?: string; beneficiaryId?: string }>();
|
||||||
|
|
||||||
const [activationCode, setActivationCode] = useState('');
|
const [activationCode, setActivationCode] = useState('');
|
||||||
const [isActivating, setIsActivating] = useState(false);
|
const [isActivating, setIsActivating] = useState(false);
|
||||||
@ -27,7 +27,13 @@ export default function ActivateScreen() {
|
|||||||
// Pre-fill beneficiary name from params if available
|
// Pre-fill beneficiary name from params if available
|
||||||
const [beneficiaryName, setBeneficiaryName] = useState(params.lovedOneName || '');
|
const [beneficiaryName, setBeneficiaryName] = useState(params.lovedOneName || '');
|
||||||
|
|
||||||
const { addLocalBeneficiary } = useBeneficiary();
|
const { addLocalBeneficiary, updateLocalBeneficiary, localBeneficiaries } = useBeneficiary();
|
||||||
|
|
||||||
|
// Check if we're activating for an existing beneficiary
|
||||||
|
const existingBeneficiaryId = params.beneficiaryId ? parseInt(params.beneficiaryId, 10) : null;
|
||||||
|
const existingBeneficiary = existingBeneficiaryId
|
||||||
|
? localBeneficiaries.find(b => b.id === existingBeneficiaryId)
|
||||||
|
: null;
|
||||||
|
|
||||||
// Demo serial for testing without real hardware
|
// Demo serial for testing without real hardware
|
||||||
const DEMO_SERIAL = 'DEMO-00000';
|
const DEMO_SERIAL = 'DEMO-00000';
|
||||||
@ -41,7 +47,7 @@ export default function ActivateScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for demo serial
|
// Check for demo serial
|
||||||
const isDemoMode = code === DEMO_SERIAL;
|
const isDemoMode = code === DEMO_SERIAL || code === 'DEMO-1234-5678';
|
||||||
|
|
||||||
// Validate code format: minimum 8 characters (or demo serial)
|
// Validate code format: minimum 8 characters (or demo serial)
|
||||||
if (!isDemoMode && code.length < 8) {
|
if (!isDemoMode && code.length < 8) {
|
||||||
@ -52,23 +58,35 @@ export default function ActivateScreen() {
|
|||||||
setIsActivating(true);
|
setIsActivating(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Demo mode shows simulated data
|
// If we have an existing beneficiary, update them with device info
|
||||||
const beneficiaryData: any = {
|
if (existingBeneficiaryId && existingBeneficiary) {
|
||||||
name: params.lovedOneName?.trim() || '',
|
await updateLocalBeneficiary(existingBeneficiaryId, {
|
||||||
};
|
hasDevices: true,
|
||||||
|
device_id: code,
|
||||||
if (isDemoMode) {
|
});
|
||||||
beneficiaryData.isDemo = true;
|
setBeneficiaryName(existingBeneficiary.name);
|
||||||
}
|
|
||||||
|
|
||||||
// If name was already provided from add-loved-one screen, skip to saving
|
|
||||||
if (params.lovedOneName && params.lovedOneName.trim()) {
|
|
||||||
await addLocalBeneficiary(beneficiaryData);
|
|
||||||
await api.setOnboardingCompleted(true);
|
|
||||||
setStep('complete');
|
setStep('complete');
|
||||||
} else {
|
} else {
|
||||||
// No name provided, show beneficiary form
|
// Creating new beneficiary
|
||||||
setStep('beneficiary');
|
const beneficiaryData: any = {
|
||||||
|
name: params.lovedOneName?.trim() || '',
|
||||||
|
hasDevices: true,
|
||||||
|
device_id: code,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isDemoMode) {
|
||||||
|
beneficiaryData.isDemo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If name was already provided from add-loved-one screen, skip to saving
|
||||||
|
if (params.lovedOneName && params.lovedOneName.trim()) {
|
||||||
|
await addLocalBeneficiary(beneficiaryData);
|
||||||
|
await api.setOnboardingCompleted(true);
|
||||||
|
setStep('complete');
|
||||||
|
} else {
|
||||||
|
// No name provided, show beneficiary form
|
||||||
|
setStep('beneficiary');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to activate:', error);
|
console.error('Failed to activate:', error);
|
||||||
@ -88,11 +106,13 @@ export default function ActivateScreen() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const code = activationCode.trim().toUpperCase();
|
const code = activationCode.trim().toUpperCase();
|
||||||
const isDemoMode = code === DEMO_SERIAL;
|
const isDemoMode = code === DEMO_SERIAL || code === 'DEMO-1234-5678';
|
||||||
|
|
||||||
// Add beneficiary WITHOUT subscription - user needs to subscribe separately
|
// Add beneficiary with device info
|
||||||
await addLocalBeneficiary({
|
await addLocalBeneficiary({
|
||||||
name: beneficiaryName.trim(),
|
name: beneficiaryName.trim(),
|
||||||
|
hasDevices: true,
|
||||||
|
device_id: code,
|
||||||
isDemo: isDemoMode,
|
isDemo: isDemoMode,
|
||||||
});
|
});
|
||||||
// Mark onboarding as completed
|
// Mark onboarding as completed
|
||||||
@ -107,8 +127,13 @@ export default function ActivateScreen() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleComplete = () => {
|
const handleComplete = () => {
|
||||||
// Navigate to main app
|
// If updating existing beneficiary, go back to their detail page
|
||||||
router.replace('/(tabs)');
|
if (existingBeneficiaryId) {
|
||||||
|
router.replace(`/(tabs)/beneficiaries/${existingBeneficiaryId}`);
|
||||||
|
} else {
|
||||||
|
// Navigate to main app
|
||||||
|
router.replace('/(tabs)');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 1: Enter activation code
|
// Step 1: Enter activation code
|
||||||
@ -118,8 +143,16 @@ export default function ActivateScreen() {
|
|||||||
<ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
|
<ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<View style={styles.placeholder} />
|
{existingBeneficiary ? (
|
||||||
<Text style={styles.title}>Activate Kit</Text>
|
<TouchableOpacity style={styles.backButton} onPress={() => router.back()}>
|
||||||
|
<Ionicons name="arrow-back" size={24} color={AppColors.textPrimary} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : (
|
||||||
|
<View style={styles.placeholder} />
|
||||||
|
)}
|
||||||
|
<Text style={styles.title}>
|
||||||
|
{existingBeneficiary ? 'Connect Sensors' : 'Activate Kit'}
|
||||||
|
</Text>
|
||||||
<View style={styles.placeholder} />
|
<View style={styles.placeholder} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@ -130,7 +163,9 @@ export default function ActivateScreen() {
|
|||||||
|
|
||||||
{/* Instructions */}
|
{/* Instructions */}
|
||||||
<Text style={styles.instructions}>
|
<Text style={styles.instructions}>
|
||||||
Enter the activation code from your WellNuo Starter Kit packaging
|
{existingBeneficiary
|
||||||
|
? `Connect sensors for ${existingBeneficiary.name}`
|
||||||
|
: 'Enter the activation code from your WellNuo Starter Kit packaging'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Input */}
|
{/* Input */}
|
||||||
@ -168,10 +203,12 @@ export default function ActivateScreen() {
|
|||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{/* Skip for now */}
|
{/* Skip for now - only show for new onboarding, not for existing beneficiary */}
|
||||||
<TouchableOpacity style={styles.skipButton} onPress={handleComplete}>
|
{!existingBeneficiary && (
|
||||||
<Text style={styles.skipButtonText}>Skip for now</Text>
|
<TouchableOpacity style={styles.skipButton} onPress={handleComplete}>
|
||||||
</TouchableOpacity>
|
<Text style={styles.skipButtonText}>Skip for now</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
@ -233,6 +270,8 @@ export default function ActivateScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Complete
|
// Step 3: Complete
|
||||||
|
const displayName = beneficiaryName || existingBeneficiary?.name || params.lovedOneName;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
@ -242,33 +281,54 @@ export default function ActivateScreen() {
|
|||||||
<Ionicons name="checkmark-circle" size={80} color={AppColors.success} />
|
<Ionicons name="checkmark-circle" size={80} color={AppColors.success} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={styles.successTitle}>Kit Activated!</Text>
|
<Text style={styles.successTitle}>
|
||||||
|
{existingBeneficiary ? 'Sensors Connected!' : 'Kit Activated!'}
|
||||||
|
</Text>
|
||||||
<Text style={styles.successMessage}>
|
<Text style={styles.successMessage}>
|
||||||
Your WellNuo kit has been successfully activated for{' '}
|
{existingBeneficiary
|
||||||
<Text style={styles.beneficiaryHighlight}>{beneficiaryName || params.lovedOneName}</Text>
|
? `Sensors have been connected for `
|
||||||
|
: `Your WellNuo kit has been successfully activated for `}
|
||||||
|
<Text style={styles.beneficiaryHighlight}>{displayName}</Text>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Next Steps */}
|
{/* Next Steps */}
|
||||||
<View style={styles.nextSteps}>
|
<View style={styles.nextSteps}>
|
||||||
<Text style={styles.nextStepsTitle}>Next Steps:</Text>
|
<Text style={styles.nextStepsTitle}>Next Steps:</Text>
|
||||||
<View style={styles.stepItem}>
|
{existingBeneficiary ? (
|
||||||
<Text style={styles.stepNumber}>1</Text>
|
<>
|
||||||
<Text style={styles.stepText}>Place sensors in your loved one's home</Text>
|
<View style={styles.stepItem}>
|
||||||
</View>
|
<Text style={styles.stepNumber}>1</Text>
|
||||||
<View style={styles.stepItem}>
|
<Text style={styles.stepText}>Connect the hub to WiFi</Text>
|
||||||
<Text style={styles.stepNumber}>2</Text>
|
</View>
|
||||||
<Text style={styles.stepText}>Connect the hub to WiFi</Text>
|
<View style={styles.stepItem}>
|
||||||
</View>
|
<Text style={styles.stepNumber}>2</Text>
|
||||||
<View style={styles.stepItem}>
|
<Text style={styles.stepText}>Subscribe to start monitoring ($49/month)</Text>
|
||||||
<Text style={styles.stepNumber}>3</Text>
|
</View>
|
||||||
<Text style={styles.stepText}>Subscribe to start monitoring ($49/month)</Text>
|
</>
|
||||||
</View>
|
) : (
|
||||||
|
<>
|
||||||
|
<View style={styles.stepItem}>
|
||||||
|
<Text style={styles.stepNumber}>1</Text>
|
||||||
|
<Text style={styles.stepText}>Place sensors in your loved one's home</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.stepItem}>
|
||||||
|
<Text style={styles.stepNumber}>2</Text>
|
||||||
|
<Text style={styles.stepText}>Connect the hub to WiFi</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.stepItem}>
|
||||||
|
<Text style={styles.stepNumber}>3</Text>
|
||||||
|
<Text style={styles.stepText}>Subscribe to start monitoring ($49/month)</Text>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Complete Button */}
|
{/* Complete Button */}
|
||||||
<TouchableOpacity style={styles.primaryButton} onPress={handleComplete}>
|
<TouchableOpacity style={styles.primaryButton} onPress={handleComplete}>
|
||||||
<Text style={styles.primaryButtonText}>Go to Dashboard</Text>
|
<Text style={styles.primaryButtonText}>
|
||||||
|
{existingBeneficiary ? 'Continue' : 'Go to Dashboard'}
|
||||||
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@ -111,7 +111,7 @@ export default function PurchaseScreen() {
|
|||||||
// 4. Payment successful! Create or update beneficiary with 'ordered' status
|
// 4. Payment successful! Create or update beneficiary with 'ordered' status
|
||||||
if (beneficiaryId) {
|
if (beneficiaryId) {
|
||||||
// Update existing beneficiary
|
// Update existing beneficiary
|
||||||
await updateLocalBeneficiary(beneficiaryId, {
|
await updateLocalBeneficiary(parseInt(beneficiaryId, 10), {
|
||||||
equipmentStatus: 'ordered',
|
equipmentStatus: 'ordered',
|
||||||
});
|
});
|
||||||
} else if (lovedOneName) {
|
} else if (lovedOneName) {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Modal, Tex
|
|||||||
import { WebView } from 'react-native-webview';
|
import { WebView } from 'react-native-webview';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { useLocalSearchParams, router } from 'expo-router';
|
import { useLocalSearchParams, router, useFocusEffect } from 'expo-router';
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
import { useBeneficiary } from '@/contexts/BeneficiaryContext';
|
import { useBeneficiary } from '@/contexts/BeneficiaryContext';
|
||||||
@ -102,6 +102,16 @@ export default function BeneficiaryDashboardScreen() {
|
|||||||
}).start();
|
}).start();
|
||||||
}, [isEditModalVisible]);
|
}, [isEditModalVisible]);
|
||||||
|
|
||||||
|
// Hide menu when navigating away from page
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
return () => {
|
||||||
|
setIsMenuVisible(false);
|
||||||
|
setIsEditModalVisible(false);
|
||||||
|
};
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
const handleEditPress = () => {
|
const handleEditPress = () => {
|
||||||
if (beneficiary) {
|
if (beneficiary) {
|
||||||
setEditForm({
|
setEditForm({
|
||||||
@ -296,11 +306,18 @@ export default function BeneficiaryDashboardScreen() {
|
|||||||
|
|
||||||
<View style={styles.headerCenter}>
|
<View style={styles.headerCenter}>
|
||||||
{currentBeneficiary && (
|
{currentBeneficiary && (
|
||||||
<View style={styles.avatarSmall}>
|
currentBeneficiary.avatar ? (
|
||||||
<Text style={styles.avatarText}>
|
<Image
|
||||||
{currentBeneficiary.name.charAt(0).toUpperCase()}
|
source={{ uri: currentBeneficiary.avatar }}
|
||||||
</Text>
|
style={styles.avatarSmallImage}
|
||||||
</View>
|
/>
|
||||||
|
) : (
|
||||||
|
<View style={styles.avatarSmall}>
|
||||||
|
<Text style={styles.avatarText}>
|
||||||
|
{currentBeneficiary.name.charAt(0).toUpperCase()}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
<View>
|
<View>
|
||||||
<Text style={styles.headerTitle}>{beneficiaryName}</Text>
|
<Text style={styles.headerTitle}>{beneficiaryName}</Text>
|
||||||
@ -545,6 +562,12 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginRight: Spacing.sm,
|
marginRight: Spacing.sm,
|
||||||
},
|
},
|
||||||
|
avatarSmallImage: {
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
borderRadius: 18,
|
||||||
|
marginRight: Spacing.sm,
|
||||||
|
},
|
||||||
avatarText: {
|
avatarText: {
|
||||||
fontSize: FontSizes.base,
|
fontSize: FontSizes.base,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
|
|||||||
@ -19,8 +19,10 @@ import { useLocalSearchParams, router } from 'expo-router';
|
|||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
|
import { usePaymentSheet } from '@stripe/stripe-react-native';
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
import { useBeneficiary } from '@/contexts/BeneficiaryContext';
|
import { useBeneficiary } from '@/contexts/BeneficiaryContext';
|
||||||
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
||||||
import { FullScreenError } from '@/components/ui/ErrorMessage';
|
import { FullScreenError } from '@/components/ui/ErrorMessage';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
@ -47,7 +49,19 @@ const isLocalBeneficiary = (id: string | number): boolean => {
|
|||||||
// Setup state types
|
// Setup state types
|
||||||
type SetupState = 'loading' | 'awaiting_equipment' | 'no_devices' | 'no_subscription' | 'ready';
|
type SetupState = 'loading' | 'awaiting_equipment' | 'no_devices' | 'no_subscription' | 'ready';
|
||||||
|
|
||||||
// No Devices Screen Component
|
// Starter Kit info
|
||||||
|
const STARTER_KIT = {
|
||||||
|
name: 'WellNuo Starter Kit',
|
||||||
|
price: '$249',
|
||||||
|
features: [
|
||||||
|
'Motion sensor (PIR)',
|
||||||
|
'Door/window sensor',
|
||||||
|
'Temperature & humidity sensor',
|
||||||
|
'WellNuo Hub',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// No Devices Screen Component - Primary: Buy Kit, Secondary: I have sensors
|
||||||
function NoDevicesScreen({
|
function NoDevicesScreen({
|
||||||
beneficiary,
|
beneficiary,
|
||||||
onActivate,
|
onActivate,
|
||||||
@ -62,38 +76,36 @@ function NoDevicesScreen({
|
|||||||
<View style={styles.setupIconContainer}>
|
<View style={styles.setupIconContainer}>
|
||||||
<Ionicons name="hardware-chip-outline" size={48} color={AppColors.primary} />
|
<Ionicons name="hardware-chip-outline" size={48} color={AppColors.primary} />
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.setupTitle}>Connect Sensors</Text>
|
<Text style={styles.setupTitle}>Get Started with WellNuo</Text>
|
||||||
<Text style={styles.setupSubtitle}>
|
<Text style={styles.setupSubtitle}>
|
||||||
To start monitoring {beneficiary.name}'s wellness, you need to connect sensors first.
|
To start monitoring {beneficiary.name}'s wellness, you need WellNuo sensors.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={styles.setupOptions}>
|
{/* Primary: Buy Kit Card */}
|
||||||
<TouchableOpacity style={styles.setupOptionCard} onPress={onActivate}>
|
<View style={styles.buyKitCard}>
|
||||||
<View style={[styles.setupOptionIcon, { backgroundColor: AppColors.primaryLighter }]}>
|
<Text style={styles.buyKitName}>{STARTER_KIT.name}</Text>
|
||||||
<Ionicons name="qr-code-outline" size={28} color={AppColors.primary} />
|
<Text style={styles.buyKitPrice}>{STARTER_KIT.price}</Text>
|
||||||
</View>
|
|
||||||
<Text style={styles.setupOptionTitle}>I have sensors</Text>
|
|
||||||
<Text style={styles.setupOptionText}>
|
|
||||||
Enter activation code to connect your WellNuo sensors
|
|
||||||
</Text>
|
|
||||||
<View style={styles.setupOptionArrow}>
|
|
||||||
<Ionicons name="arrow-forward" size={20} color={AppColors.primary} />
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity style={styles.setupOptionCard} onPress={onGetSensors}>
|
<View style={styles.buyKitFeatures}>
|
||||||
<View style={[styles.setupOptionIcon, { backgroundColor: AppColors.accentLight }]}>
|
{STARTER_KIT.features.map((feature, index) => (
|
||||||
<Ionicons name="cart-outline" size={28} color={AppColors.accent} />
|
<View key={index} style={styles.buyKitFeatureRow}>
|
||||||
</View>
|
<Ionicons name="checkmark-circle" size={18} color={AppColors.success} />
|
||||||
<Text style={styles.setupOptionTitle}>Get sensors</Text>
|
<Text style={styles.buyKitFeatureText}>{feature}</Text>
|
||||||
<Text style={styles.setupOptionText}>
|
</View>
|
||||||
Order WellNuo sensor kit for comprehensive monitoring
|
))}
|
||||||
</Text>
|
</View>
|
||||||
<View style={styles.setupOptionArrow}>
|
|
||||||
<Ionicons name="arrow-forward" size={20} color={AppColors.accent} />
|
<TouchableOpacity style={styles.buyKitButton} onPress={onGetSensors}>
|
||||||
</View>
|
<Ionicons name="cart" size={20} color={AppColors.white} />
|
||||||
|
<Text style={styles.buyKitButtonText}>Buy Now - {STARTER_KIT.price}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Secondary: I already have sensors */}
|
||||||
|
<TouchableOpacity style={styles.alreadyHaveLink} onPress={onActivate}>
|
||||||
|
<Text style={styles.alreadyHaveLinkText}>I already have sensors</Text>
|
||||||
|
<Ionicons name="arrow-forward" size={16} color={AppColors.textSecondary} />
|
||||||
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -239,6 +251,7 @@ export default function BeneficiaryDetailScreen() {
|
|||||||
const isLocal = useMemo(() => id ? isLocalBeneficiary(id) : false, [id]);
|
const isLocal = useMemo(() => id ? isLocalBeneficiary(id) : false, [id]);
|
||||||
|
|
||||||
// Determine setup state
|
// Determine setup state
|
||||||
|
// Flow: No devices → Connect Sensors → Subscription → Dashboard
|
||||||
const setupState = useMemo((): SetupState => {
|
const setupState = useMemo((): SetupState => {
|
||||||
if (isLoading) return 'loading';
|
if (isLoading) return 'loading';
|
||||||
if (!beneficiary) return 'loading';
|
if (!beneficiary) return 'loading';
|
||||||
@ -249,14 +262,14 @@ export default function BeneficiaryDetailScreen() {
|
|||||||
return 'awaiting_equipment';
|
return 'awaiting_equipment';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if has devices
|
// Check if has devices - required first step
|
||||||
const hasDevices = beneficiary.hasDevices ||
|
const hasDevices = beneficiary.hasDevices ||
|
||||||
(beneficiary.devices && beneficiary.devices.length > 0) ||
|
(beneficiary.devices && beneficiary.devices.length > 0) ||
|
||||||
beneficiary.device_id;
|
beneficiary.device_id;
|
||||||
|
|
||||||
if (!hasDevices) return 'no_devices';
|
if (!hasDevices) return 'no_devices';
|
||||||
|
|
||||||
// Check subscription
|
// Check subscription - required after devices connected
|
||||||
const subscription = beneficiary.subscription;
|
const subscription = beneficiary.subscription;
|
||||||
if (!subscription || subscription.status === 'none' || subscription.status === 'expired') {
|
if (!subscription || subscription.status === 'none' || subscription.status === 'expired') {
|
||||||
return 'no_subscription';
|
return 'no_subscription';
|
||||||
@ -326,6 +339,16 @@ export default function BeneficiaryDetailScreen() {
|
|||||||
loadBeneficiary();
|
loadBeneficiary();
|
||||||
}, [loadBeneficiary]);
|
}, [loadBeneficiary]);
|
||||||
|
|
||||||
|
// Sync beneficiary data when localBeneficiaries changes (especially after avatar update)
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLocal && id && beneficiary) {
|
||||||
|
const updated = localBeneficiaries.find(b => b.id === parseInt(id, 10));
|
||||||
|
if (updated && updated.avatar !== beneficiary.avatar) {
|
||||||
|
setBeneficiary(updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [localBeneficiaries, id, isLocal]);
|
||||||
|
|
||||||
const handleRefresh = useCallback(() => {
|
const handleRefresh = useCallback(() => {
|
||||||
setIsRefreshing(true);
|
setIsRefreshing(true);
|
||||||
loadBeneficiary(false);
|
loadBeneficiary(false);
|
||||||
@ -333,17 +356,17 @@ export default function BeneficiaryDetailScreen() {
|
|||||||
|
|
||||||
const handleActivateSensors = () => {
|
const handleActivateSensors = () => {
|
||||||
router.push({
|
router.push({
|
||||||
pathname: '/(tabs)/beneficiaries/[id]/activate',
|
pathname: '/(auth)/activate',
|
||||||
params: { id: id! },
|
params: { beneficiaryId: id!, lovedOneName: beneficiary?.name },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGetSensors = () => {
|
const handleGetSensors = () => {
|
||||||
// For now, show info or redirect to purchase
|
// Navigate to purchase screen with beneficiary info
|
||||||
toast.info(
|
router.push({
|
||||||
'Get WellNuo Sensors',
|
pathname: '/(auth)/purchase',
|
||||||
'WellNuo sensor kits include motion sensors, door sensors, and temperature monitors. Visit wellnuo.com to order.'
|
params: { beneficiaryId: id!, lovedOneName: beneficiary?.name },
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMarkReceived = async () => {
|
const handleMarkReceived = async () => {
|
||||||
@ -696,29 +719,6 @@ export default function BeneficiaryDetailScreen() {
|
|||||||
{/* Dropdown Menu */}
|
{/* Dropdown Menu */}
|
||||||
{isMenuVisible && (
|
{isMenuVisible && (
|
||||||
<View style={styles.dropdownMenu}>
|
<View style={styles.dropdownMenu}>
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.dropdownItem}
|
|
||||||
onPress={() => {
|
|
||||||
setIsMenuVisible(false);
|
|
||||||
setCurrentBeneficiary(beneficiary);
|
|
||||||
router.push('/(tabs)/chat');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Ionicons name="chatbubbles-outline" size={20} color={AppColors.textPrimary} />
|
|
||||||
<Text style={styles.dropdownItemText}>Chat</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.dropdownItem}
|
|
||||||
onPress={() => {
|
|
||||||
setIsMenuVisible(false);
|
|
||||||
router.push(`/(tabs)/beneficiaries/${id}/dashboard`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Ionicons name="stats-chart-outline" size={20} color={AppColors.textPrimary} />
|
|
||||||
<Text style={styles.dropdownItemText}>Dashboard</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.dropdownItem}
|
style={styles.dropdownItem}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@ -1004,6 +1004,71 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
// Buy Kit Card Styles
|
||||||
|
buyKitCard: {
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: AppColors.white,
|
||||||
|
borderRadius: BorderRadius.xl,
|
||||||
|
padding: Spacing.xl,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: AppColors.primary,
|
||||||
|
alignItems: 'center',
|
||||||
|
...Shadows.md,
|
||||||
|
},
|
||||||
|
buyKitName: {
|
||||||
|
fontSize: FontSizes.xl,
|
||||||
|
fontWeight: FontWeights.bold,
|
||||||
|
color: AppColors.textPrimary,
|
||||||
|
marginBottom: Spacing.sm,
|
||||||
|
},
|
||||||
|
buyKitPrice: {
|
||||||
|
fontSize: FontSizes['3xl'],
|
||||||
|
fontWeight: FontWeights.bold,
|
||||||
|
color: AppColors.primary,
|
||||||
|
marginBottom: Spacing.lg,
|
||||||
|
},
|
||||||
|
buyKitFeatures: {
|
||||||
|
width: '100%',
|
||||||
|
marginBottom: Spacing.lg,
|
||||||
|
gap: Spacing.sm,
|
||||||
|
},
|
||||||
|
buyKitFeatureRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: Spacing.sm,
|
||||||
|
},
|
||||||
|
buyKitFeatureText: {
|
||||||
|
fontSize: FontSizes.sm,
|
||||||
|
color: AppColors.textPrimary,
|
||||||
|
},
|
||||||
|
buyKitButton: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: Spacing.sm,
|
||||||
|
backgroundColor: AppColors.primary,
|
||||||
|
paddingVertical: Spacing.md,
|
||||||
|
paddingHorizontal: Spacing.xl,
|
||||||
|
borderRadius: BorderRadius.lg,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
buyKitButtonText: {
|
||||||
|
fontSize: FontSizes.lg,
|
||||||
|
fontWeight: FontWeights.semibold,
|
||||||
|
color: AppColors.white,
|
||||||
|
},
|
||||||
|
alreadyHaveLink: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: Spacing.xs,
|
||||||
|
marginTop: Spacing.xl,
|
||||||
|
paddingVertical: Spacing.md,
|
||||||
|
},
|
||||||
|
alreadyHaveLinkText: {
|
||||||
|
fontSize: FontSizes.base,
|
||||||
|
color: AppColors.textSecondary,
|
||||||
|
},
|
||||||
// Equipment Progress Styles
|
// Equipment Progress Styles
|
||||||
progressContainer: {
|
progressContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|||||||
@ -199,12 +199,8 @@ export default function HomeScreen() {
|
|||||||
|
|
||||||
const handleBeneficiaryPress = (beneficiary: Beneficiary) => {
|
const handleBeneficiaryPress = (beneficiary: Beneficiary) => {
|
||||||
setCurrentBeneficiary(beneficiary);
|
setCurrentBeneficiary(beneficiary);
|
||||||
// If equipment is not active yet, show status page instead of dashboard
|
// Always go to beneficiary detail page (which includes MockDashboard)
|
||||||
if (beneficiary.equipmentStatus && ['ordered', 'shipped'].includes(beneficiary.equipmentStatus)) {
|
router.push(`/(tabs)/beneficiaries/${beneficiary.id}`);
|
||||||
router.push(`/(tabs)/beneficiaries/${beneficiary.id}`);
|
|
||||||
} else {
|
|
||||||
router.push(`/(tabs)/beneficiaries/${beneficiary.id}/dashboard`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleActivate = (beneficiary: Beneficiary) => {
|
const handleActivate = (beneficiary: Beneficiary) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user