import { HealthProgressRing } from '@/components/health/HealthProgressRing'; import { BasicInfoTab } from '@/components/health/tabs/BasicInfoTab'; import { CheckupRecordsTab } from '@/components/health/tabs/CheckupRecordsTab'; import { HealthHistoryTab } from '@/components/health/tabs/HealthHistoryTab'; import { MedicalRecordsTab } from '@/components/health/tabs/MedicalRecordsTab'; import { ConfirmationSheet } from '@/components/ui/ConfirmationSheet'; import { HeaderBar } from '@/components/ui/HeaderBar'; import { Colors } from '@/constants/Colors'; import { ROUTES } from '@/constants/Routes'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useI18n } from '@/hooks/useI18n'; import { fetchFamilyGroup, joinFamilyGroup, selectFamilyGroup, } from '@/store/familyHealthSlice'; import { fetchHealthHistory, selectHealthHistoryProgress } from '@/store/healthSlice'; import { DEFAULT_MEMBER_NAME } from '@/store/userSlice'; import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { Stack, useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Pressable, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; export default function HealthProfileScreen() { const router = useRouter(); const insets = useSafeAreaInsets(); const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const colorTokens = Colors[theme]; const { t } = useI18n(); const dispatch = useAppDispatch(); const { ensureLoggedIn } = useAuthGuard(); const glassAvailable = isLiquidGlassAvailable(); const [activeTab, setActiveTab] = useState(0); const [joinModalVisible, setJoinModalVisible] = useState(false); const [inviteCodeInput, setInviteCodeInput] = useState(''); const [selectedRelationship, setSelectedRelationship] = useState(''); const [isJoining, setIsJoining] = useState(false); const [joinError, setJoinError] = useState(null); // Redux state const familyGroup = useAppSelector(selectFamilyGroup); const medicalRecords = useAppSelector((state) => state.health.medicalRecords); const records = medicalRecords?.records || []; const prescriptions = medicalRecords?.prescriptions || []; // Calculate Medical Records Count const medicalRecordsCount = useMemo(() => records.length + prescriptions.length, [records, prescriptions]); // 亲属关系选项 const relationshipOptions = useMemo(() => [ { key: 'spouse', label: t('familyGroup.relationships.spouse') }, { key: 'father', label: t('familyGroup.relationships.father') }, { key: 'mother', label: t('familyGroup.relationships.mother') }, { key: 'son', label: t('familyGroup.relationships.son') }, { key: 'daughter', label: t('familyGroup.relationships.daughter') }, { key: 'grandfather', label: t('familyGroup.relationships.grandfather') }, { key: 'grandmother', label: t('familyGroup.relationships.grandmother') }, { key: 'grandson', label: t('familyGroup.relationships.grandson') }, { key: 'granddaughter', label: t('familyGroup.relationships.granddaughter') }, { key: 'brother', label: t('familyGroup.relationships.brother') }, { key: 'sister', label: t('familyGroup.relationships.sister') }, { key: 'uncle', label: t('familyGroup.relationships.uncle') }, { key: 'aunt', label: t('familyGroup.relationships.aunt') }, { key: 'nephew', label: t('familyGroup.relationships.nephew') }, { key: 'niece', label: t('familyGroup.relationships.niece') }, { key: 'cousin', label: t('familyGroup.relationships.cousin') }, { key: 'other', label: t('familyGroup.relationships.other') }, ], [t]); // Mock user data - in a real app this would come from Redux/Context const userProfile = useAppSelector((state) => state.user.profile); const displayName = userProfile.name?.trim() ? userProfile.name : DEFAULT_MEMBER_NAME; const avatarUrl = userProfile.avatar || 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/seal-avatar/2.jpeg'; // 从 Redux 获取健康史进度 const healthHistoryProgress = useAppSelector(selectHealthHistoryProgress); // Mock health data const healthData = { bmi: userProfile.weight && userProfile.height ? (parseFloat(userProfile.weight) / Math.pow(parseFloat(userProfile.height) / 100, 2)).toFixed(1) : '--', height: userProfile.height ? `${parseFloat(userProfile.height).toFixed(1)}` : '--', weight: userProfile.weight ? `${parseFloat(userProfile.weight).toFixed(1)}` : '--', waist: userProfile.waistCircumference ? `${parseFloat(userProfile.waistCircumference.toString()).toFixed(1)}` : '--', status: '健康状况良好', statusDesc: '请继续保持良好的生活习惯', statusMessage: '您的健康状况不错哦~' }; // Calculate Basic Info completion percentage const basicInfoProgress = useMemo(() => { let filledCount = 0; const totalFields = 3; // height, weight, waist if (userProfile.height && parseFloat(userProfile.height) > 0) filledCount++; if (userProfile.weight && parseFloat(userProfile.weight) > 0) filledCount++; if (userProfile.waistCircumference && parseFloat(userProfile.waistCircumference.toString()) > 0) filledCount++; return Math.round((filledCount / totalFields) * 100); }, [userProfile.height, userProfile.weight, userProfile.waistCircumference]); // 初始化获取家庭组信息和健康史数据 useEffect(() => { dispatch(fetchFamilyGroup()); dispatch(fetchHealthHistory()); }, [dispatch]); // 重置弹窗状态 useEffect(() => { if (!joinModalVisible) { setInviteCodeInput(''); setSelectedRelationship(''); setJoinError(null); } }, [joinModalVisible]); // 打开加入弹窗 const handleOpenJoin = useCallback(async () => { const ok = await ensureLoggedIn(); if (!ok) return; setJoinModalVisible(true); }, [ensureLoggedIn]); // 提交加入家庭组 const handleSubmitJoin = useCallback(async () => { if (isJoining) return; const ok = await ensureLoggedIn(); if (!ok) return; const code = inviteCodeInput.trim().toUpperCase(); if (!code) { setJoinError(t('familyGroup.errors.emptyCode')); return; } if (!selectedRelationship) { setJoinError(t('familyGroup.errors.emptyRelationship')); return; } // 获取选中关系的显示文本 const relationshipLabel = relationshipOptions.find(r => r.key === selectedRelationship)?.label || selectedRelationship; setIsJoining(true); setJoinError(null); try { await dispatch(joinFamilyGroup({ inviteCode: code, relationship: relationshipLabel })).unwrap(); await dispatch(fetchFamilyGroup()); setJoinModalVisible(false); Toast.success(t('familyGroup.success')); } catch (error) { const message = typeof error === 'string' ? error : '加入失败,请检查邀请码是否正确'; setJoinError(message); } finally { setIsJoining(false); } }, [dispatch, ensureLoggedIn, inviteCodeInput, isJoining, selectedRelationship, relationshipOptions, t]); const gradientColors: [string, string] = theme === 'dark' ? ['#1f2230', '#10131e'] : [colorTokens.backgroundGradientStart, colorTokens.backgroundGradientEnd]; const tabs = [ t('health.tabs.healthProfile.basicInfo'), t('health.tabs.healthProfile.healthHistory'), // t('health.tabs.healthProfile.medicalRecords'), t('health.tabs.healthProfile.checkupRecords'), t('health.tabs.healthProfile.medicineBox') ]; const tabIcons = ["person", "time", "folder", "clipboard", "medkit"]; const handleTabPress = (index: number) => { if (index === 3) { // Handle Medicine Box tab specially router.push('/medications/manage-medications'); return; } setActiveTab(index); }; const renderActiveTab = () => { switch (activeTab) { case 0: return ; case 1: return ; case 2: return ; case 3: return ; default: return ; } }; return ( {/* 加入家庭组按钮 - 仅在未加入家庭组时显示 */} {!familyGroup && ( {glassAvailable ? ( 加入 ) : ( 加入 )} )} } /> {/* Top Section with Avatar and Status */} {displayName} router.push(ROUTES.HEALTH_FAMILY_INVITE)} > {/* Action Buttons - Replaced with HealthProgressRing */} {/* Family Invite Banner */} router.push(ROUTES.HEALTH_FAMILY_INVITE)} > {t('health.tabs.healthProfile.subtitle')} {/* Tab/Segment Control */} {tabs.map((tab, index) => ( handleTabPress(index)} activeOpacity={0.7} > {tab} ))} {/* Active Tab Content */} {renderActiveTab()} {/* Privacy Notice Footer */} {t('health.tabs.healthProfile.privacyNotice')} {/* 加入家庭组弹窗 */} setJoinModalVisible(false)} onConfirm={handleSubmitJoin} title={t('familyGroup.joinTitle')} description={t('familyGroup.joinDescription')} confirmText={isJoining ? t('familyGroup.joining') : t('familyGroup.joinButton')} cancelText={t('familyGroup.cancel')} loading={isJoining} content={ {/* 邀请码输入 */} setInviteCodeInput(text.toUpperCase())} autoCapitalize="characters" autoCorrect={false} keyboardType="default" maxLength={12} /> {/* 关系选择标签 */} {t('familyGroup.relationshipLabel')} {/* 关系选项网格 - 固定高度可滚动 */} {relationshipOptions.map((option) => { const isSelected = selectedRelationship === option.key; return ( setSelectedRelationship(option.key)} > {option.label} ); })} {/* 错误提示 */} {joinError && joinModalVisible ? ( {joinError} ) : null} } /> ); } const styles = StyleSheet.create({ container: { flex: 1, }, scrollContent: { paddingHorizontal: 16, paddingBottom: 100, }, topSection: { marginBottom: 20, }, avatarRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 10, }, miniAvatarContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#5B4CFF', paddingVertical: 4, paddingHorizontal: 4, paddingRight: 12, borderRadius: 20, }, miniAvatar: { width: 24, height: 24, borderRadius: 12, marginRight: 6, borderWidth: 1, borderColor: '#FFF', }, miniAvatarName: { color: '#FFF', fontSize: 12, fontWeight: 'bold', }, addButton: { width: 28, height: 28, borderRadius: 14, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginLeft: 8, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 2, elevation: 2, }, actionButtonsRow: { flexDirection: 'row', justifyContent: 'space-around', marginTop: 24, marginBottom: 12, }, inviteBanner: { backgroundColor: '#FFFFFF', borderRadius: 20, padding: 16, marginBottom: 20, shadowColor: '#5B4CFF', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.05, shadowRadius: 8, elevation: 2, }, inviteContent: { flexDirection: 'row', alignItems: 'center', }, inviteIconContainer: { marginRight: 8, }, inviteText: { flex: 1, fontSize: 13, color: '#1F2138', fontWeight: '600', }, segmentControl: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16, paddingHorizontal: 18, }, segmentItem: { alignItems: 'center', }, segmentIconPlaceholder: { width: 48, height: 48, borderRadius: 12, backgroundColor: '#F3F4F6', alignItems: 'center', justifyContent: 'center', marginBottom: 4, }, segmentIconActive: { backgroundColor: '#E0E7FF', }, segmentText: { fontSize: 14, color: '#6B7280', }, segmentTextActive: { color: '#5B4CFF', fontWeight: 'bold', }, privacyNoticeContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: 20, paddingHorizontal: 16, marginTop: 32, marginBottom: 16, }, privacyIconWrapper: { marginRight: 6, }, privacyNoticeText: { fontSize: 12, color: '#9CA3AF', textAlign: 'center', lineHeight: 18, }, joinButtonGlass: { paddingHorizontal: 14, paddingVertical: 8, borderRadius: 16, minWidth: 60, alignItems: 'center', justifyContent: 'center', borderWidth: StyleSheet.hairlineWidth, borderColor: 'rgba(255,255,255,0.45)', }, joinButtonLabel: { fontSize: 12, fontWeight: '700', color: '#0f1528', letterSpacing: 0.5, fontFamily: 'AliBold', }, joinButtonFallback: { backgroundColor: 'rgba(255,255,255,0.7)', }, // 加入家庭组弹窗样式 joinModalContent: { gap: 12, }, inviteCodeInput: { backgroundColor: '#f8fafc', borderRadius: 14, paddingHorizontal: 16, paddingVertical: 14, fontSize: 18, fontWeight: '700', letterSpacing: 2, color: '#0f1528', textAlign: 'center', }, relationshipLabel: { fontSize: 14, fontWeight: '600', color: '#374151', marginTop: 4, marginBottom: 2, }, relationshipScrollView: { maxHeight: 160, borderRadius: 12, backgroundColor: '#fafafa', }, relationshipGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, padding: 8, }, relationshipChip: { paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20, backgroundColor: '#f3f4f6', borderWidth: 1.5, borderColor: 'transparent', }, relationshipChipSelected: { backgroundColor: '#ede9fe', borderColor: '#8b5cf6', }, relationshipChipText: { fontSize: 14, color: '#6b7280', fontWeight: '500', }, relationshipChipTextSelected: { color: '#7c3aed', fontWeight: '600', }, modalError: { marginTop: 6, fontSize: 12, color: '#ef4444', }, });