import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { HealthHistoryCategory } from '@/services/healthProfile'; import { HistoryItemDetail, fetchHealthHistory, saveHealthHistoryCategory, selectHealthLoading, selectHistoryData, updateHistoryData, } from '@/store/healthSlice'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ActivityIndicator, Alert, KeyboardAvoidingView, Modal, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import DateTimePickerModal from 'react-native-modal-datetime-picker'; import { palette } from '../../../constants/Colors'; // Translation Keys for Recommendations const RECOMMENDATION_KEYS: Record = { allergy: ['penicillin', 'sulfonamides', 'peanuts', 'seafood', 'pollen', 'dustMites', 'alcohol', 'mango'], disease: ['hypertension', 'diabetes', 'asthma', 'heartDisease', 'gastritis', 'migraine'], surgery: ['appendectomy', 'cesareanSection', 'tonsillectomy', 'fractureRepair', 'none'], familyDisease: ['hypertension', 'diabetes', 'cancer', 'heartDisease', 'stroke', 'alzheimers'], }; interface HistoryItemProps { title: string; categoryKey: string; data: { hasHistory: boolean | null; items: HistoryItemDetail[]; }; onPress?: () => void; } function HistoryItem({ title, categoryKey, data, onPress }: HistoryItemProps) { const { t } = useTranslation(); const translateItemName = (name: string) => { const keys = RECOMMENDATION_KEYS[categoryKey]; if (keys && keys.includes(name)) { return t(`health.tabs.healthProfile.history.recommendationItems.${categoryKey}.${name}`); } return name; }; const hasItems = data.hasHistory === true && data.items.length > 0; return ( {/* Header Row */} {title} {!hasItems && ( {data.hasHistory === null ? t('health.tabs.healthProfile.history.pending') : data.hasHistory === false ? t('health.tabs.healthProfile.history.modal.none') : t('health.tabs.healthProfile.history.modal.yesNoDetails')} )} {/* List of Items */} {hasItems && ( {data.items.map(item => ( {translateItemName(item.name)} {item.date && ( {dayjs(item.date).format('YYYY-MM-DD')} )} ))} )} ); } export function HealthHistoryTab() { const { t } = useTranslation(); const dispatch = useAppDispatch(); // 从 Redux store 获取健康史数据和加载状态 const historyData = useAppSelector(selectHistoryData); const isLoading = useAppSelector(selectHealthLoading); // Modal State const [modalVisible, setModalVisible] = useState(false); const [currentType, setCurrentType] = useState(null); const [tempHasHistory, setTempHasHistory] = useState(null); const [tempItems, setTempItems] = useState([]); const [isSaving, setIsSaving] = useState(false); // Date Picker State const [isDatePickerVisible, setDatePickerVisibility] = useState(false); const [currentEditingId, setCurrentEditingId] = useState(null); // 初始化时从服务端获取健康史数据 useEffect(() => { dispatch(fetchHealthHistory()); }, [dispatch]); const historyItems = [ { title: t('health.tabs.healthProfile.history.allergy'), key: 'allergy' }, { title: t('health.tabs.healthProfile.history.disease'), key: 'disease' }, { title: t('health.tabs.healthProfile.history.surgery'), key: 'surgery' }, { title: t('health.tabs.healthProfile.history.familyDisease'), key: 'familyDisease' }, ]; // Helper to translate item (try to find key, fallback to item itself) const translateItem = (type: string, item: string) => { // Check if item is a predefined key const keys = RECOMMENDATION_KEYS[type]; if (keys && keys.includes(item)) { return t(`health.tabs.healthProfile.history.recommendationItems.${type}.${item}`); } // Fallback for manual input return item; }; // Open Modal const handleItemPress = (key: string) => { setCurrentType(key); const currentData = historyData[key]; setTempHasHistory(currentData.hasHistory); // Deep copy items to avoid reference issues setTempItems(currentData.items.map(item => ({ ...item }))); setModalVisible(true); }; // Close Modal const handleCloseModal = () => { setModalVisible(false); setCurrentType(null); }; // Save Data const handleSave = async () => { if (currentType) { // Filter out empty items const validItems = tempItems.filter(item => item.name.trim() !== ''); // If "No" history is selected, clear items const finalItems = tempHasHistory === false ? [] : validItems; setIsSaving(true); try { // 先乐观更新本地状态 dispatch(updateHistoryData({ type: currentType, data: { hasHistory: tempHasHistory, items: finalItems, }, })); // 同步到服务端 await dispatch(saveHealthHistoryCategory({ category: currentType as HealthHistoryCategory, data: { hasHistory: tempHasHistory ?? false, items: finalItems.map(item => ({ name: item.name, date: item.date ? dayjs(item.date).format('YYYY-MM-DD') : undefined, isRecommendation: item.isRecommendation, })), }, })).unwrap(); handleCloseModal(); } catch (error: any) { // 如果保存失败,显示错误提示(本地数据已更新,下次打开会从服务端同步) Alert.alert( t('health.tabs.healthProfile.history.modal.saveError') || '保存失败', error?.message || '请稍后重试', [{ text: t('health.tabs.healthProfile.history.modal.ok') || '确定' }] ); } finally { setIsSaving(false); } } }; // Add Item (Manual or Recommendation) const addItem = (name: string = '', isRecommendation: boolean = false) => { // Avoid duplicates for recommendations if already exists if (isRecommendation && tempItems.some(item => item.name === name)) { return; } const newItem: HistoryItemDetail = { id: Date.now().toString() + Math.random().toString(), name, isRecommendation }; setTempItems([...tempItems, newItem]); }; // Remove Item const removeItem = (id: string) => { setTempItems(tempItems.filter(item => item.id !== id)); }; // Update Item Name const updateItemName = (id: string, text: string) => { setTempItems(tempItems.map(item => item.id === id ? { ...item, name: text } : item )); }; // Date Picker Handlers const showDatePicker = (id: string) => { setCurrentEditingId(id); setDatePickerVisibility(true); }; const hideDatePicker = () => { setDatePickerVisibility(false); setCurrentEditingId(null); }; const handleConfirmDate = (date: Date) => { if (currentEditingId) { setTempItems(tempItems.map(item => item.id === currentEditingId ? { ...item, date: date.toISOString() } : item )); } hideDatePicker(); }; return ( {/* Glow effect background */} {/* Header */} {t('health.tabs.healthProfile.healthHistory')} {isLoading && } {/* List */} {historyItems.map((item) => ( handleItemPress(item.key)} /> ))} {/* Edit Modal */} {/* Modal Header */} {currentType ? t(`health.tabs.healthProfile.history.${currentType}`) : ''} {/* Question: Do you have history? */} {t('health.tabs.healthProfile.history.modal.question', { type: currentType ? t(`health.tabs.healthProfile.history.${currentType}`) : '' })} setTempHasHistory(true)} > {t('health.tabs.healthProfile.history.modal.yes')} setTempHasHistory(false)} > {t('health.tabs.healthProfile.history.modal.no')} {/* Conditional Content */} {tempHasHistory === true && currentType && ( {/* Recommendations */} {RECOMMENDATION_KEYS[currentType] && ( {t('health.tabs.healthProfile.history.modal.recommendations')} {RECOMMENDATION_KEYS[currentType].map((tagKey, index) => ( addItem(tagKey, true)} > {t(`health.tabs.healthProfile.history.recommendationItems.${currentType}.${tagKey}`)} ))} )} {/* History List Items */} {t('health.tabs.healthProfile.history.modal.addDetails')} {tempItems.map((item) => ( updateItemName(item.id, text)} editable={!item.isRecommendation} /> removeItem(item.id)} style={styles.deleteButton}> showDatePicker(item.id)} > {item.date ? dayjs(item.date).format('YYYY-MM-DD') : t('health.tabs.healthProfile.history.modal.selectDate')} ))} {/* Add Button */} addItem()}> {t('health.tabs.healthProfile.history.modal.addItem')} )} {/* Save Button */} {isSaving ? ( ) : ( {t('health.tabs.healthProfile.history.modal.save')} )} ); } const styles = StyleSheet.create({ container: { marginBottom: 16, position: 'relative', }, glowContainer: { position: 'absolute', top: 20, left: 20, right: 20, bottom: 20, alignItems: 'center', justifyContent: 'center', zIndex: -1, }, glow: { width: '90%', height: '90%', backgroundColor: palette.purple[200], opacity: 0.3, borderRadius: 40, transform: [{ scale: 1.05 }], shadowColor: palette.purple[500], shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0.4, shadowRadius: 20, }, card: { backgroundColor: '#FFFFFF', borderRadius: 24, padding: 20, shadowColor: palette.purple[100], shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.6, shadowRadius: 24, elevation: 4, borderWidth: 1, borderColor: '#F5F3FF', }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, paddingHorizontal: 4, }, headerTitle: { fontSize: 18, fontFamily: 'AliBold', color: palette.gray[900], fontWeight: '600', }, list: { backgroundColor: '#FFFFFF', }, itemContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 16, paddingHorizontal: 4, }, itemContainerWithList: { flexDirection: 'column', alignItems: 'stretch', justifyContent: 'flex-start', }, itemHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', width: '100%', }, itemLeft: { flexDirection: 'row', alignItems: 'center', }, indicator: { width: 4, height: 14, borderRadius: 2, marginRight: 12, }, itemTitle: { fontSize: 16, color: palette.gray[700], fontFamily: 'AliRegular', }, itemStatus: { fontSize: 14, color: palette.gray[300], fontFamily: 'AliRegular', textAlign: 'right', maxWidth: 150, }, itemStatusActive: { color: palette.purple[600], fontWeight: '500', }, subListContainer: { marginTop: 12, paddingLeft: 16, // Align with title (4px indicator + 12px margin) }, subItemRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 6, }, subItemDot: { width: 6, height: 6, borderRadius: 3, backgroundColor: palette.purple[300], marginRight: 8, }, subItemName: { flex: 1, fontSize: 15, color: palette.gray[800], fontFamily: 'AliRegular', fontWeight: '500', }, subItemDate: { fontSize: 13, color: palette.gray[400], fontFamily: 'AliRegular', }, // Modal Styles modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center', padding: 20, }, modalContent: { width: '100%', backgroundColor: '#FFFFFF', borderRadius: 24, padding: 24, maxHeight: '85%', // Increased height shadowColor: '#000', shadowOffset: { width: 0, height: 10 }, shadowOpacity: 0.2, shadowRadius: 20, elevation: 10, }, modalHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20, }, modalTitle: { fontSize: 20, fontFamily: 'AliBold', color: palette.gray[900], fontWeight: '600', }, closeButton: { padding: 4, }, questionText: { fontSize: 16, color: palette.gray[700], marginBottom: 12, fontFamily: 'AliRegular', }, radioGroup: { flexDirection: 'row', marginBottom: 24, }, radioButton: { flex: 1, paddingVertical: 12, borderWidth: 1, borderColor: palette.gray[200], borderRadius: 12, alignItems: 'center', marginRight: 8, }, radioButtonActive: { backgroundColor: palette.purple[50], borderColor: palette.purple[500], }, radioText: { fontSize: 16, color: palette.gray[600], fontWeight: '500', }, radioTextActive: { color: palette.purple[600], fontWeight: '600', }, detailsContainer: { marginTop: 4, }, sectionLabel: { fontSize: 14, color: palette.gray[500], marginBottom: 12, marginTop: 8, fontFamily: 'AliRegular', }, recommendationContainer: { marginBottom: 20, }, tagsContainer: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, }, tag: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20, backgroundColor: '#F5F7FA', }, tagText: { fontSize: 14, color: palette.gray[600], fontFamily: 'AliRegular', }, listContainer: { marginTop: 8, }, listItemCard: { backgroundColor: '#F9FAFB', borderRadius: 16, padding: 16, marginBottom: 12, borderWidth: 1, borderColor: palette.gray[100], }, listItemHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, }, listItemNameInput: { flex: 1, fontSize: 16, fontWeight: '600', color: palette.gray[900], fontFamily: 'AliBold', padding: 0, }, deleteButton: { padding: 4, }, datePickerTrigger: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FFFFFF', paddingHorizontal: 12, paddingVertical: 10, borderRadius: 12, borderWidth: 1, borderColor: palette.gray[200], }, dateText: { marginLeft: 8, fontSize: 14, color: palette.gray[900], fontFamily: 'AliRegular', }, placeholderText: { color: palette.gray[400], }, addItemButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: 12, borderWidth: 1, borderColor: palette.purple[200], borderRadius: 12, borderStyle: 'dashed', backgroundColor: palette.purple[25], marginTop: 4, marginBottom: 20, }, addItemText: { marginLeft: 8, fontSize: 14, color: palette.purple[600], fontWeight: '500', }, modalFooter: { marginTop: 8, }, saveButton: { borderRadius: 16, overflow: 'hidden', shadowColor: palette.purple[500], shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8, elevation: 4, }, saveButtonDisabled: { shadowOpacity: 0, }, saveButtonGradient: { paddingVertical: 14, alignItems: 'center', }, saveButtonText: { fontSize: 16, color: '#FFFFFF', fontWeight: '600', fontFamily: 'AliBold', }, });