diff --git a/src/nutrimetas/app/(app)/(root)/DailyGoal.tsx b/src/nutrimetas/app/(app)/(root)/DailyGoal.tsx new file mode 100644 index 0000000..ca49148 --- /dev/null +++ b/src/nutrimetas/app/(app)/(root)/DailyGoal.tsx @@ -0,0 +1,269 @@ +import { StyleSheet, TouchableOpacity, FlatList, View, Text, Image, TextInput } from 'react-native'; +import React, { useState, useEffect } from 'react'; +import Colors from '@/constants/Colors'; +import firestore from '@react-native-firebase/firestore'; +import { useRouter } from 'expo-router'; +import Icon from 'react-native-vector-icons/Ionicons'; +import { useGlobalSearchParams } from 'expo-router'; +import { CheckBox } from 'react-native-elements'; + +const DailyGoal = () => { + const router = useRouter(); + const { patientId } = useGlobalSearchParams(); + const [goals, setGoals] = useState([]); + const [loading, setLoading] = useState(true); + const [selectedGoal, setSelectedGoal] = useState<{ [key: string]: boolean }>({}); + const [textInputValues, setTextInputValues] = useState<{ [key: string]: string }>({}); + + useEffect(() => { + if (patientId) { + const unsubscribe = firestore() + .collection('Patient') + .doc(patientId.toString()) + .onSnapshot((snapshot) => { + const patientData = snapshot.data(); + const patientGoals = patientData && patientData.Goals ? patientData.Goals : []; + + if (patientGoals.length > 0) { + fetchGoalsFromFirebase(patientGoals); + } else { + setLoading(false); + console.log('El paciente no tiene metas.'); + } + }); + return () => unsubscribe(); + } else { + setLoading(false); + } + }, [patientId]); + + const fetchGoalsFromFirebase = async (patientGoals: any) => { + const goalsFromFirebase = []; + for (const goalId of patientGoals) { + if (typeof goalId.id === 'string') { + const goalDoc = await firestore().collection('Goal').doc(goalId.id).get(); + const goalSelectId = goalDoc.id; + if (goalDoc.exists) { + const goalData = goalDoc.data(); + if (goalData) { + const description = await buildDailyGoal(goalData); + goalsFromFirebase.push({ ...goalData, description, goalSelectId }); + } else { + console.error('Goal data is undefined for goal ID:', goalId.id); + } + } + } else { + console.error('Invalid goal ID:', goalId); + } + } + setGoals(goalsFromFirebase); + setLoading(false); + }; + + const fetchReferenceData = async (collection: string, docId: string) => { + const doc = await firestore().collection(collection).doc(docId).get(); + const data = doc.exists ? doc.data() : null; + return data; + }; + + const buildDailyGoal = async (goalData:any) => { + try { + const [rubricData, actionData, amountData, portionData] = await Promise.all([ + fetchReferenceData('Rubric', goalData.Rubric), + fetchReferenceData('Action', goalData.Action), + fetchReferenceData('Amount', goalData.Amount), + fetchReferenceData('Portion', goalData.Portion), + ]); + if (!rubricData || !actionData || !amountData || !portionData) { + console.error('Missing data for building description'); + return ''; + } + + let typePrefix; + if (actionData.Name.includes('consumo')) { + typePrefix = 'Consumiste'; + } else { + typePrefix = 'Realizaste'; + } + const today = 'hoy?'; + + // Elimina mayúsculas innecesarias + const rubricName = rubricData.Name.toLowerCase(); + const portionName = amountData.Value === 1 ? portionData.Name.toLowerCase() : portionData.Plural.toLowerCase(); + + // Determinar el género + let article; + if (amountData.Value === 1) { + article = portionData.Gender === 'M' ? 'un' : 'una'; + } else { + article = portionData.Gender === 'M' ? 'unos' : 'unas'; + } + + let interrogative; + if (amountData.Value !== 1) { + interrogative = portionData.Gender === 'M' ? 'Cuántos' : 'Cuántas'; + } + + // Construcción de la oración basada en la cantidad + let description; + if (amountData.Value !== 1) { + description = `${interrogative} ${portionName} de ${rubricName} consumiste ${today}`; + } else { + // Actividad fisica + description = `${typePrefix} ${article} ${rubricName} ${today}`; + } + // Mayúscula solo para la primera letra de la oración + const result = description.charAt(0).toUpperCase() + description.slice(1); + return result; + } catch (error) { + console.error('Error building description:', error); + return ''; + } + }; + + const confirmDaily = () => { + router.back(); + }; + + const GoalCheckbox = (goalId: string) => { + setSelectedGoal(prevState => ({ + ...prevState, + [goalId]: !prevState[goalId] // Cambia el estado del checkbox + })); + }; + + // El numero solo se admite de 0-9 + const handleTextInputChange = (goalId: string, text: string) => { + const newText = text.replace(/[^0-9]/g, ''); + if (newText.length <= 1) { + setTextInputValues(prevState => ({ + ...prevState, + [goalId]: newText, + })); + } + }; + + return ( + + + router.back()}> + + + Registro de hoy + + {loading ? ( + Cargando... + ) : goals.length === 0 ? ( + + No tiene metas pendientes. + + ) : ( + ( + + + + {item.description} + + {item.description.includes('Cuán') ? ( + handleTextInputChange(item.goalSelectId, text)} + /> + ) : ( + GoalCheckbox(item.goalSelectId)} + /> + )} + + )} + keyExtractor={(item, index) => `${item.title}-${index}`} + /> + )} + + + + + ); +} + +export default DailyGoal; + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: 50, + paddingLeft: 20, + paddingRight: 20, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 10, + }, + title: { + fontSize: 28, + fontWeight: 'bold', + textAlign: 'left', + marginLeft: 10, + }, + item: { + flexDirection: 'row', + alignItems: 'center', + margin: '2%', + borderBottomWidth: 1 + }, + itemImage: { + width: 60, + height: 60, + marginRight: 10, + }, + goalDetails: { + flex: 1 + }, + confirmDailyButton: { + position: 'absolute', + bottom: 20, + right: 20, + height: 60, + borderRadius: 30, + backgroundColor: Colors.green, + justifyContent: 'center', + alignItems: 'center', + elevation: 5, + paddingHorizontal: 20, + }, + textInput: { + backgroundColor: Colors.white, + borderColor: 'gray' , + borderWidth: 1, + borderRadius: 5, + marginRight: 24, + padding: 5, + fontSize: 16, + width: 20, + height: 28, + textAlign: 'center' , + }, + itemDescription: { + + fontSize: 14 + }, + emptyContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center' + }, + emptyText: { + fontSize: 18, + textAlign: 'center' + }, +}); diff --git a/src/nutrimetas/app/(app)/(root)/GoalList.tsx b/src/nutrimetas/app/(app)/(root)/GoalList.tsx index 498c967..7e8942c 100644 --- a/src/nutrimetas/app/(app)/(root)/GoalList.tsx +++ b/src/nutrimetas/app/(app)/(root)/GoalList.tsx @@ -1,13 +1,16 @@ import { StyleSheet, TouchableOpacity, FlatList, Image, ActivityIndicator } from 'react-native'; import { View, Text, useThemeColor } from '@/components/Themed' import React, { useState, useEffect, useContext } from 'react'; +import Colors from '@/constants/Colors'; import firestore from '@react-native-firebase/firestore'; import { useNavigation } from '@react-navigation/native'; import { useRouter } from 'expo-router'; -import Colors from '@/constants/Colors'; import Icon from 'react-native-vector-icons/Ionicons'; -import { SessionContext } from '@/shared/LoginSession'; // Importa el contexto de la sesión +import { SessionContext } from '@/shared/LoginSession'; import { useGlobalSearchParams } from 'expo-router'; +import { CheckBox} from 'react-native-elements'; +import FontAwesome from '@expo/vector-icons/FontAwesome'; +import DateTimePicker from '@react-native-community/datetimepicker'; const GoalList = () => { const router = useRouter(); @@ -16,9 +19,26 @@ const GoalList = () => { const { role } = useContext(SessionContext); const [goals, setGoals] = useState([]); const [loading, setLoading] = useState(true); + const [showPopup, setShowPopup] = useState(false); + + // estados para las fechas + const [showStartDatePicker, setShowStartDatePicker] = useState(false); + const [startDate, setStartDate] = useState(new Date()); + const [originalGoals, setOriginalGoals] = useState([]); + const [originalGoalsSaved, setOriginalGoalsSaved] = useState(false); + const [showBackdrop, setShowBackdrop] = useState(false); + + useEffect(() => { + // Guarda las metas originales solo la primera vez que se cargan + if (!originalGoalsSaved && goals.length > 0) { + setOriginalGoals(goals); + setOriginalGoalsSaved(true); + } + }, [goals, originalGoalsSaved]); const arrowColor = useThemeColor({ light: Colors.black, dark: Colors.white }, 'text'); useEffect(() => { + // Maneja la suscripción a los cambios de Patient en Firestore if (patientId) { const unsubscribe = firestore() .collection('Patient') @@ -30,14 +50,12 @@ const GoalList = () => { if (patientGoals.length > 0) { fetchGoalsFromFirebase(patientGoals); } else { - setLoading(false); // No hay metas, actualiza el estado de carga + setLoading(false); console.log('El paciente no tiene metas.'); } }); - return () => unsubscribe(); } else { - // console.error('patientId is undefined'); setLoading(false); } }, [patientId]); @@ -46,9 +64,9 @@ const GoalList = () => { const goalsFromFirebase = []; for (const goalId of patientGoals) { - // Verificar si goalId es una cadena (ID de objetivo) if (typeof goalId.id === 'string') { const goalDoc = await firestore().collection('Goal').doc(goalId.id).get(); + //console.log('Datos de la meta:', goalDoc.data()); const goalSelectId = goalDoc.id; if (goalDoc.exists) { const goalData = goalDoc.data(); @@ -66,7 +84,8 @@ const GoalList = () => { } setGoals(goalsFromFirebase); - setLoading(false); // Actualiza el estado de carga + + setLoading(false); }; const buildTitle = async (rubricRef: string) => { @@ -84,14 +103,16 @@ const GoalList = () => { } } catch (error) { console.error('Error fetching rubric:', error); - return 'Meta'; // O valor predeterminado + return 'Meta'; } }; const fetchReferenceData = async (collection: string, docId: string) => { const doc = await firestore().collection(collection).doc(docId).get(); - return doc.exists ? doc.data() : null; + const data = doc.exists ? doc.data() : null; + return data; }; + const buildDescription = async (goalData: any) => { try { @@ -108,12 +129,14 @@ const GoalList = () => { console.error('Missing data for building description'); return ''; } + let typePrefix; if (typeData.Name === 'Aumentar' || typeData.Name === 'Disminuir') { typePrefix = 'a'; } else { typePrefix = 'en'; } + const portionName = amountData.Value === 1 ? portionData.Name : portionData.Plural; return `${typeData.Name} ${actionData.Name} ${rubricData.Name} ${typePrefix} ${amountData.Value} ${portionName} ${frequencyData.Name}`; } catch (error) { @@ -131,6 +154,75 @@ const GoalList = () => { router.replace({ pathname: '/assingGoal', params: { patientId: patientId } }); // navigation.navigate('assingGoal', { sessionDocId: patientId }); }; + + const handleDailyGoal = () => { + console.log('daily register'); + router.push({ pathname: '/DailyGoal', params: { patientId: patientId } }); + console.log({ pathname: '/DailyGoal', params: { sessionDocId: patientId } }); + }; + + const handleFilterPress = () => { + setShowPopup(true); + setShowBackdrop(true); + }; + + const handleCancel = () => { + setShowPopup(false); + setShowBackdrop(false); + }; + + const handleConfirm = () => { + const formattedStartDate = startDate.toISOString(); + + // Calcular la fecha final como 7 días después de la fecha de inicio + const endDate = new Date(startDate); + endDate.setDate(endDate.getDate() + 7); + /*const formattedEndDate = endDate.toISOString(); + console.log('Fecha de Inicio:', formattedStartDate); + console.log('Fecha Límite:', formattedEndDate); */ + + filterGoalsByDateRange(startDate, endDate); + setShowPopup(false); + setShowBackdrop(false); + }; + + const handleStartDateChange = (event, selectedDate) => { + setShowStartDatePicker(false); + if (selectedDate) { + setStartDate(selectedDate); + } + }; + + const handleReset = () => { + setShowPopup(false); + setGoals(originalGoals); // Restablece las metas a los valores originales + setShowBackdrop(false); + } + + const filterGoalsByDateRange = async (startDate, endDate) => { + if (patientId) { + setLoading(true); + try { + // Ajustar las fechas de inicio y fin para incluir la primera y última hora + const adjustedStartDate = new Date(startDate); + adjustedStartDate.setHours(0, 0, 0, 0); + const adjustedEndDate = new Date(endDate); + adjustedEndDate.setHours(23, 59, 59, 999); + + // Filtrar las metas originales por las fechas ajustadas + const filteredGoals = originalGoals.filter(goal => { + const goalStartDate = new Date(goal.StartDate.toDate()); + return goalStartDate >= adjustedStartDate && goalStartDate <= adjustedEndDate; + }); + setGoals(filteredGoals); + } catch (error) { + console.error("Error filtering goals:", error); + } finally { + setLoading(false); + } + } + }; + if (loading) { return ( @@ -156,7 +248,48 @@ const GoalList = () => { Metas + + + + {showBackdrop && } + {/* Ventana emergente */} + {showPopup && ( + + + + Filtros + + Fecha de Inicio + setShowStartDatePicker(true)}> + {startDate.toDateString()} + + + {showStartDatePicker && ( + + )} + + + Salir + + + Aplicar + + + + Eliminar Filtros + + + )} + ( @@ -173,13 +306,18 @@ const GoalList = () => { )} - keyExtractor={(item, index) => `${item.title}-${index}`} // Aseguramos un key único - /> + keyExtractor={(item, index) => `${item.title}-${index}`} + /> {role === 'professional' && ( )} + {role === 'patient' && ( + + + + )} ); } @@ -187,11 +325,74 @@ const GoalList = () => { export default GoalList; const styles = StyleSheet.create({ + backdrop: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: Colors.backdrop, + zIndex: 998, + }, + popupContainer: { + position: 'absolute', + flex: 1, + backgroundColor: Colors.white, + padding: 20, + marginTop: '20%', + borderRadius: 10, + borderWidth: 1, + borderColor: Colors.black, + alignItems: 'center', + justifyContent: 'center', + zIndex: 999, + left: '45%', + }, + filtersHeader: { + position: 'absolute', + top: 10, + left: 10, + }, + filterTitle: { + fontSize: 18, + fontWeight: 'bold', + textAlign: 'left', + marginLeft: 10, + }, + dateTitle:{ + marginTop: 10, + marginBottom: -10, + }, + button: { + backgroundColor: Colors.lightblue, + padding: 10, + borderRadius: 5, + marginTop: 10, + }, + buttonText: { + color: Colors.white, + fontWeight: 'bold', + textAlign: 'center', + }, + cancelButton: { + backgroundColor: Colors.red, + padding: 10, + borderRadius: 5, + marginTop: 10, + marginRight: 5, + }, + confirmButton: { + backgroundColor: Colors.green, + padding: 10, + borderRadius: 5, + marginTop: 10, + marginLeft: 5, + }, container: { flex: 1, paddingTop: 50, paddingLeft: 20, - paddingRight: 20, // Added paddingRight for space for the tittle + paddingRight: 20, }, header: { flexDirection: 'row', @@ -202,7 +403,7 @@ const styles = StyleSheet.create({ fontSize: 28, fontWeight: 'bold', textAlign: 'left', - marginLeft: 10, // Margin on the left to separate the button from the title + marginLeft: 10, }, item: { flexDirection: 'row', @@ -213,7 +414,20 @@ const styles = StyleSheet.create({ itemImage: { width: 60, height: 60, - marginRight: 10 + marginRight: 10, + }, + filterContainer: { + position: 'absolute', + top: 0, + right: 0, + width: 30, + height: 25, + marginTop: 8, + marginRight: 8, + }, + filterImage: { + width: 30, + height: 25, }, goalDetails: { flex: 1 @@ -250,9 +464,31 @@ const styles = StyleSheet.create({ width: 60, height: 60, borderRadius: 30, - backgroundColor: 'green', + backgroundColor: Colors.green, justifyContent: 'center', alignItems: 'center', elevation: 5, }, + registerDayButton: { + position: 'absolute', + bottom: 20, + right: 20, + height: 60, + borderRadius: 30, + backgroundColor: Colors.green, + justifyContent: 'center', + alignItems: 'center', + elevation: 5, + paddingHorizontal: 20, + }, + datePickerStyle: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + padding: 10, + marginVertical: 10, + borderWidth: 1, + borderRadius: 5, + borderColor: Colors.gray, + }, }); \ No newline at end of file diff --git a/src/nutrimetas/app/(app)/(root)/_layout.tsx b/src/nutrimetas/app/(app)/(root)/_layout.tsx index 078a2b2..4f0b942 100644 --- a/src/nutrimetas/app/(app)/(root)/_layout.tsx +++ b/src/nutrimetas/app/(app)/(root)/_layout.tsx @@ -54,6 +54,7 @@ function RootLayoutNav() { + ); diff --git a/src/nutrimetas/app/(app)/(root)/addPatient.tsx b/src/nutrimetas/app/(app)/(root)/addPatient.tsx index 496c266..0a1bd07 100644 --- a/src/nutrimetas/app/(app)/(root)/addPatient.tsx +++ b/src/nutrimetas/app/(app)/(root)/addPatient.tsx @@ -414,4 +414,4 @@ const styles = StyleSheet.create({ error: { color: Colors.red, }, -}); +}); \ No newline at end of file diff --git a/src/nutrimetas/constants/Colors.ts b/src/nutrimetas/constants/Colors.ts index 4955f86..697453f 100644 --- a/src/nutrimetas/constants/Colors.ts +++ b/src/nutrimetas/constants/Colors.ts @@ -28,5 +28,6 @@ export default { lightGray: '#ccc', transparent: '#ffffff00', red: '#FF0000', - black: '#000000' + black: '#000000', + backdrop: '#00000080' , }; diff --git a/src/nutrimetas/package-lock.json b/src/nutrimetas/package-lock.json index 44d38f0..be2591e 100644 --- a/src/nutrimetas/package-lock.json +++ b/src/nutrimetas/package-lock.json @@ -35,6 +35,7 @@ "react-hook-form": "^7.51.4", "react-native": "0.73.6", "react-native-element-dropdown": "^2.12.0", + "react-native-elements": "^3.4.3", "react-native-flash-message": "^0.4.2", "react-native-gifted-chat": "^2.4.0", "react-native-image-pan-zoom": "^2.1.12", @@ -6561,7 +6562,6 @@ }, "node_modules/@types/react-native": { "version": "0.70.19", - "dev": true, "license": "MIT", "dependencies": { "@types/react": "*" @@ -6569,7 +6569,6 @@ }, "node_modules/@types/react-native-vector-icons": { "version": "6.4.18", - "dev": true, "license": "MIT", "dependencies": { "@types/react": "*", @@ -12720,6 +12719,11 @@ "version": "4.0.8", "license": "MIT" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.throttle": { "version": "4.1.1", "license": "MIT" @@ -13941,6 +13945,14 @@ "node": ">=4" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/ora": { "version": "5.4.1", "license": "MIT", @@ -14722,6 +14734,35 @@ "react-native": "*" } }, + "node_modules/react-native-elements": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.3.tgz", + "integrity": "sha512-VtZc25EecPZyUBER85zFK9ZbY6kkUdcm1ZwJ9hdoGSCr1R/GFgxor4jngOcSYeMvQ+qimd5No44OVJW3rSJECA==", + "hasInstallScript": true, + "dependencies": { + "@types/react-native-vector-icons": "^6.4.6", + "color": "^3.1.2", + "deepmerge": "^4.2.2", + "hoist-non-react-statics": "^3.3.2", + "lodash.isequal": "^4.5.0", + "opencollective-postinstall": "^2.0.3", + "react-native-ratings": "8.0.4", + "react-native-size-matters": "^0.3.1" + }, + "peerDependencies": { + "react-native-safe-area-context": ">= 3.0.0", + "react-native-vector-icons": ">7.0.0" + } + }, + "node_modules/react-native-elements/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/react-native-flash-message": { "version": "0.4.2", "license": "MIT", @@ -14885,6 +14926,18 @@ "react-native": "*" } }, + "node_modules/react-native-ratings": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-8.0.4.tgz", + "integrity": "sha512-Xczu5lskIIRD6BEdz9A0jDRpEck/SFxRqiglkXi0u67yAtI1/pcJC76P4MukCbT8K4BPVl+42w83YqXBoBRl7A==", + "dependencies": { + "lodash": "^4.17.15" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-safe-area-context": { "version": "4.8.2", "license": "MIT", @@ -14916,6 +14969,14 @@ "react-native": ">=0.71.0" } }, + "node_modules/react-native-size-matters": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz", + "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==", + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/react-native-toast-message": { "version": "2.2.0", "license": "MIT", @@ -17224,4 +17285,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/nutrimetas/package.json b/src/nutrimetas/package.json index 77211f2..8371b9b 100644 --- a/src/nutrimetas/package.json +++ b/src/nutrimetas/package.json @@ -40,6 +40,7 @@ "react-hook-form": "^7.51.4", "react-native": "0.73.6", "react-native-element-dropdown": "^2.12.0", + "react-native-elements": "^3.4.3", "react-native-flash-message": "^0.4.2", "react-native-gifted-chat": "^2.4.0", "react-native-image-pan-zoom": "^2.1.12",