WellNuo/app/(tabs)/profile/privacy.tsx
Sergei abcc380984 Sync all changes - profile restructure and scheme updates
- Restructured profile screens location
- Updated beneficiary detail page
- Updated API service
- Updated all scheme files (MainScheme, ENV API, Discussion, AppStore, SysAnal)
- Added PageHeader component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 16:48:07 -08:00

563 lines
17 KiB
TypeScript

import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
Switch,
TouchableOpacity,
Alert,
TextInput,
Modal,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { SafeAreaView } from 'react-native-safe-area-context';
import { router } from 'expo-router';
import { useAuth } from '@/contexts/AuthContext';
import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme';
import { PageHeader } from '@/components/PageHeader';
interface SecurityItemProps {
icon: keyof typeof Ionicons.glyphMap;
iconColor: string;
iconBgColor: string;
title: string;
description: string;
onPress: () => void;
rightElement?: React.ReactNode;
}
function SecurityItem({
icon,
iconColor,
iconBgColor,
title,
description,
onPress,
rightElement,
}: SecurityItemProps) {
return (
<TouchableOpacity style={styles.securityRow} onPress={onPress}>
<View style={[styles.iconContainer, { backgroundColor: iconBgColor }]}>
<Ionicons name={icon} size={20} color={iconColor} />
</View>
<View style={styles.securityContent}>
<Text style={styles.securityTitle}>{title}</Text>
<Text style={styles.securityDescription}>{description}</Text>
</View>
{rightElement || <Ionicons name="chevron-forward" size={20} color={AppColors.textMuted} />}
</TouchableOpacity>
);
}
export default function PrivacyScreen() {
const { logout } = useAuth();
const [twoFactor, setTwoFactor] = useState(false);
const [biometric, setBiometric] = useState(false);
const [showPasswordModal, setShowPasswordModal] = useState(false);
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const handleChangePassword = () => {
setShowPasswordModal(true);
};
const handlePasswordSubmit = () => {
if (!currentPassword || !newPassword || !confirmPassword) {
Alert.alert('Error', 'Please fill in all fields');
return;
}
if (newPassword !== confirmPassword) {
Alert.alert('Error', 'New passwords do not match');
return;
}
if (newPassword.length < 8) {
Alert.alert('Error', 'Password must be at least 8 characters');
return;
}
setShowPasswordModal(false);
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
Alert.alert('Success', 'Your password has been changed successfully.');
};
const handleEnable2FA = (value: boolean) => {
if (value) {
Alert.alert(
'Enable Two-Factor Authentication',
'This will add an extra layer of security to your account. You will need to enter a code from your authenticator app when signing in.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Enable',
onPress: () => {
setTwoFactor(true);
Alert.alert(
'Scan QR Code',
'Open your authenticator app (Google Authenticator, Authy, etc.) and scan the QR code.',
[{ text: 'Done' }]
);
}
},
]
);
} else {
Alert.alert(
'Disable Two-Factor Authentication',
'Are you sure? This will make your account less secure.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Disable',
style: 'destructive',
onPress: () => setTwoFactor(false)
},
]
);
}
};
const handleManageSessions = () => {
Alert.alert(
'Active Sessions',
'You are currently signed in on:\n\n' +
'• iPhone 14 Pro (This device)\n Last active: Just now\n\n' +
'• Chrome on MacBook Pro\n Last active: 2 hours ago\n\n' +
'• Safari on iPad\n Last active: 3 days ago',
[
{ text: 'Close' },
{
text: 'Sign Out All',
style: 'destructive',
onPress: () => {
Alert.alert(
'Sign Out All Devices',
'This will sign you out of all devices including this one.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Sign Out All',
style: 'destructive',
onPress: async () => {
await logout();
router.replace('/(auth)/login');
}
},
]
);
}
},
]
);
};
const handleExportData = () => {
Alert.alert(
'Export Your Data',
'We will prepare a downloadable file containing all your data. This may take a few minutes.\n\nYou will receive an email when your data is ready.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Request Export',
onPress: () => Alert.alert('Request Sent', 'You will receive an email when your data export is ready.')
},
]
);
};
const handleDeleteAccount = () => {
Alert.alert(
'Delete Account',
'Are you absolutely sure you want to delete your account?\n\n' +
'• All your data will be permanently deleted\n' +
'• You will lose access to all beneficiary data\n' +
'• This action cannot be undone',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Delete Account',
style: 'destructive',
onPress: () => {
Alert.alert(
'Final Confirmation',
'Type "DELETE" to confirm account deletion.',
[{ text: 'Cancel', style: 'cancel' }]
);
}
},
]
);
};
return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<PageHeader title="Privacy & Security" />
<ScrollView showsVerticalScrollIndicator={false}>
{/* Password & Authentication */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Authentication</Text>
<View style={styles.card}>
<SecurityItem
icon="key"
iconColor="#3B82F6"
iconBgColor="#DBEAFE"
title="Change Password"
description="Update your account password"
onPress={handleChangePassword}
/>
<View style={styles.divider} />
<SecurityItem
icon="shield-checkmark"
iconColor="#10B981"
iconBgColor="#D1FAE5"
title="Two-Factor Authentication"
description={twoFactor ? 'Enabled' : 'Not enabled'}
onPress={() => handleEnable2FA(!twoFactor)}
rightElement={
<Switch
value={twoFactor}
onValueChange={handleEnable2FA}
trackColor={{ false: '#E5E7EB', true: AppColors.primaryLight }}
thumbColor={twoFactor ? AppColors.primary : '#9CA3AF'}
/>
}
/>
<View style={styles.divider} />
<SecurityItem
icon="finger-print"
iconColor="#8B5CF6"
iconBgColor="#EDE9FE"
title="Biometric Login"
description="Face ID / Touch ID"
onPress={() => setBiometric(!biometric)}
rightElement={
<Switch
value={biometric}
onValueChange={setBiometric}
trackColor={{ false: '#E5E7EB', true: AppColors.primaryLight }}
thumbColor={biometric ? AppColors.primary : '#9CA3AF'}
/>
}
/>
</View>
</View>
{/* Session Management */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Sessions</Text>
<View style={styles.card}>
<SecurityItem
icon="phone-portrait"
iconColor="#F59E0B"
iconBgColor="#FEF3C7"
title="Manage Sessions"
description="View and manage active devices"
onPress={handleManageSessions}
/>
</View>
</View>
{/* Data & Privacy */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Data & Privacy</Text>
<View style={styles.card}>
<SecurityItem
icon="download"
iconColor="#6366F1"
iconBgColor="#E0E7FF"
title="Export Your Data"
description="Download a copy of your data"
onPress={handleExportData}
/>
<View style={styles.divider} />
<SecurityItem
icon="eye-off"
iconColor="#EC4899"
iconBgColor="#FCE7F3"
title="Data Sharing"
description="Control who can see your data"
onPress={() => Alert.alert('Data Sharing', 'Your data is only shared with authorized caregivers and healthcare providers you designate.')}
/>
</View>
</View>
{/* Login History */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Recent Activity</Text>
<View style={styles.card}>
<View style={styles.activityItem}>
<View style={styles.activityDot} />
<View style={styles.activityContent}>
<Text style={styles.activityTitle}>Login from iPhone</Text>
<Text style={styles.activityMeta}>Today at 2:34 PM San Francisco, CA</Text>
</View>
</View>
<View style={styles.activityItem}>
<View style={[styles.activityDot, styles.activityDotOld]} />
<View style={styles.activityContent}>
<Text style={styles.activityTitle}>Login from MacBook</Text>
<Text style={styles.activityMeta}>Yesterday at 10:15 AM San Francisco, CA</Text>
</View>
</View>
<View style={styles.activityItem}>
<View style={[styles.activityDot, styles.activityDotOld]} />
<View style={styles.activityContent}>
<Text style={styles.activityTitle}>Password changed</Text>
<Text style={styles.activityMeta}>Dec 1, 2024 at 4:22 PM</Text>
</View>
</View>
</View>
</View>
{/* Danger Zone */}
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: AppColors.error }]}>Danger Zone</Text>
<View style={styles.card}>
<TouchableOpacity style={styles.dangerButton} onPress={handleDeleteAccount}>
<Ionicons name="trash" size={20} color={AppColors.error} />
<Text style={styles.dangerButtonText}>Delete Account</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
{/* Password Change Modal */}
<Modal
visible={showPasswordModal}
animationType="slide"
presentationStyle="pageSheet"
onRequestClose={() => setShowPasswordModal(false)}
>
<SafeAreaView style={styles.modalContainer}>
<View style={styles.modalHeader}>
<TouchableOpacity onPress={() => setShowPasswordModal(false)}>
<Text style={styles.cancelText}>Cancel</Text>
</TouchableOpacity>
<Text style={styles.modalTitle}>Change Password</Text>
<TouchableOpacity onPress={handlePasswordSubmit}>
<Text style={styles.saveText}>Save</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.modalContent}>
<View style={styles.inputGroup}>
<Text style={styles.label}>Current Password</Text>
<TextInput
style={styles.input}
value={currentPassword}
onChangeText={setCurrentPassword}
placeholder="Enter current password"
placeholderTextColor={AppColors.textMuted}
secureTextEntry
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>New Password</Text>
<TextInput
style={styles.input}
value={newPassword}
onChangeText={setNewPassword}
placeholder="Enter new password"
placeholderTextColor={AppColors.textMuted}
secureTextEntry
/>
<Text style={styles.hint}>Must be at least 8 characters</Text>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Confirm New Password</Text>
<TextInput
style={styles.input}
value={confirmPassword}
onChangeText={setConfirmPassword}
placeholder="Confirm new password"
placeholderTextColor={AppColors.textMuted}
secureTextEntry
/>
</View>
<View style={styles.passwordRequirements}>
<Text style={styles.requirementsTitle}>Password Requirements:</Text>
<Text style={styles.requirementItem}> At least 8 characters</Text>
<Text style={styles.requirementItem}> Mix of letters and numbers recommended</Text>
<Text style={styles.requirementItem}> Special characters for extra security</Text>
</View>
</ScrollView>
</SafeAreaView>
</Modal>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: AppColors.surface,
},
section: {
marginTop: Spacing.md,
},
sectionTitle: {
fontSize: FontSizes.sm,
fontWeight: '600',
color: AppColors.textSecondary,
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.sm,
textTransform: 'uppercase',
},
card: {
backgroundColor: AppColors.background,
},
securityRow: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.lg,
},
iconContainer: {
width: 40,
height: 40,
borderRadius: BorderRadius.md,
justifyContent: 'center',
alignItems: 'center',
},
securityContent: {
flex: 1,
marginLeft: Spacing.md,
},
securityTitle: {
fontSize: FontSizes.base,
fontWeight: '500',
color: AppColors.textPrimary,
},
securityDescription: {
fontSize: FontSizes.xs,
color: AppColors.textMuted,
marginTop: 2,
},
divider: {
height: 1,
backgroundColor: AppColors.border,
marginLeft: Spacing.lg + 40 + Spacing.md,
},
activityItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.lg,
},
activityDot: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: AppColors.success,
marginRight: Spacing.md,
},
activityDotOld: {
backgroundColor: AppColors.textMuted,
},
activityContent: {
flex: 1,
},
activityTitle: {
fontSize: FontSizes.sm,
fontWeight: '500',
color: AppColors.textPrimary,
},
activityMeta: {
fontSize: FontSizes.xs,
color: AppColors.textMuted,
marginTop: 2,
},
dangerButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: Spacing.md,
},
dangerButtonText: {
fontSize: FontSizes.base,
fontWeight: '500',
color: AppColors.error,
marginLeft: Spacing.sm,
},
// Modal styles
modalContainer: {
flex: 1,
backgroundColor: AppColors.surface,
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.md,
backgroundColor: AppColors.background,
borderBottomWidth: 1,
borderBottomColor: AppColors.border,
},
modalTitle: {
fontSize: FontSizes.lg,
fontWeight: '600',
color: AppColors.textPrimary,
},
cancelText: {
fontSize: FontSizes.base,
color: AppColors.textSecondary,
},
saveText: {
fontSize: FontSizes.base,
fontWeight: '600',
color: AppColors.primary,
},
modalContent: {
padding: Spacing.lg,
},
inputGroup: {
marginBottom: Spacing.lg,
},
label: {
fontSize: FontSizes.sm,
fontWeight: '600',
color: AppColors.textPrimary,
marginBottom: Spacing.xs,
},
input: {
backgroundColor: AppColors.background,
borderRadius: BorderRadius.md,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.md,
fontSize: FontSizes.base,
color: AppColors.textPrimary,
borderWidth: 1,
borderColor: AppColors.border,
},
hint: {
fontSize: FontSizes.xs,
color: AppColors.textMuted,
marginTop: Spacing.xs,
},
passwordRequirements: {
backgroundColor: AppColors.background,
borderRadius: BorderRadius.lg,
padding: Spacing.md,
marginTop: Spacing.md,
},
requirementsTitle: {
fontSize: FontSizes.sm,
fontWeight: '600',
color: AppColors.textSecondary,
marginBottom: Spacing.sm,
},
requirementItem: {
fontSize: FontSizes.sm,
color: AppColors.textMuted,
marginBottom: 4,
},
});