From 3d5a951bdba75edc3546c9b09cf34fc82f36b22b Mon Sep 17 00:00:00 2001 From: tahmid-saj Date: Sun, 16 Jun 2024 20:36:52 -0400 Subject: [PATCH] nutrient prediction added to tracker --- .../shared/button/button.styles.jsx | 2 +- .../nutrients-info.component.jsx | 104 +++++++++++++++--- .../nutrition-tracker.context.js | 30 ++++- .../nutrition-tracker.context.js | 30 ++++- .../nutrition-tracker.action.js | 26 ++++- .../nutrition-tracker.validations.js | 70 +++++++++++- 6 files changed, 238 insertions(+), 24 deletions(-) diff --git a/src/components/shared/button/button.styles.jsx b/src/components/shared/button/button.styles.jsx index 446c0bd..83953b7 100644 --- a/src/components/shared/button/button.styles.jsx +++ b/src/components/shared/button/button.styles.jsx @@ -61,6 +61,6 @@ export const RoundedButton = styled(BaseButton)` export const ButtonsContainer = styled.div` display: flex; - justify-content: space-between; + justify-content: space-evenly; align-items: center; ` \ No newline at end of file diff --git a/src/components/shared/nutrient-predictor/nutrients-info/nutrients-info.component.jsx b/src/components/shared/nutrient-predictor/nutrients-info/nutrients-info.component.jsx index 7632af8..258580e 100644 --- a/src/components/shared/nutrient-predictor/nutrients-info/nutrients-info.component.jsx +++ b/src/components/shared/nutrient-predictor/nutrients-info/nutrients-info.component.jsx @@ -6,6 +6,13 @@ import { NutrientPredictorContext } from "../../../../contexts/shared/nutrient-p import { Divider, Typography } from "@mui/material"; import OutlinedCard from "../../mui/card/card.component.jsx"; import { COLOR_CODES } from "../../../../utils/constants/shared.constants.js"; +import Button from "../../button/button.component.jsx"; +import { ButtonsContainer } from "../../button/button.styles.jsx"; +import { useDispatch, useSelector } from "react-redux"; +import { selectCurrentUser } from "../../../../store/shared/user/user.selector.js" +import { NutritionTrackerContext } from "../../../../contexts/signed-in/nutrition-tracker/nutrition-tracker.context.js"; +import { addDayTrackedFromPrediction, setFormInputMicronutrients } from "../../../../store/signed-out/nutrition-tracker/nutrition-tracker.action.js"; +import { selectNutritionTrackedDays } from "../../../../store/signed-out/nutrition-tracker/nutrition-tracker.selector.js"; const outlinedCardStyles = { backgroundColor: COLOR_CODES.card.infoCard @@ -13,35 +20,90 @@ const outlinedCardStyles = { const NutrientsInfo = () => { const { nutrientPredictions } = useContext(NutrientPredictorContext); + const currentUser = useSelector(selectCurrentUser) - console.log(nutrientPredictions) + const { addDayTrackedFromPrediction: addDayTrackedFromPredictionSignedIn } = useContext(NutritionTrackerContext) + + const dispatch = useDispatch() + const nutritionTrackedDays = useSelector(selectNutritionTrackedDays) + + + const handleAddToTracker = (prediction) => { + const currentDate = new Date() + const predictionNutritionInfo = { + dateTracked: currentDate.toISOString().split('T')[0], + calories: Number(prediction.calories), + macronutrients: { + carbohydrates: Number(prediction.macronutrients.carbohydratesTotalG), + protein: Number(prediction.macronutrients.proteinG), + fat: Number(prediction.macronutrients.fatTotalG) + }, + micronutrients: [ + { + name: "Sodium", + amount: Number(prediction.micronutrients.sodiumMG), + unit: "mg" + }, + { + name: "Potassium", + amount: Number(prediction.micronutrients.potassiumMG), + unit: "mg" + }, + { + name: "Cholesterol", + amount: Number(prediction.micronutrients.cholesterolMg), + unit: "mg" + }, + { + name: "Fiber", + amount: Number(prediction.micronutrients.fiberG), + unit: "g" + }, + { + name: "Sugar", + amount: Number(prediction.micronutrients.sugarG), + unit: "g" + }, + ] + } + + // signed in + if (currentUser) { + addDayTrackedFromPredictionSignedIn(predictionNutritionInfo) + + // signed out + } else { + dispatch(addDayTrackedFromPrediction(nutritionTrackedDays, predictionNutritionInfo)) + dispatch(setFormInputMicronutrients([])) + } + } return ( nutrientPredictions.map((prediction, index) => { return ( - {`${prediction.name.toUpperCase()}`} -

+ {`${prediction.name.toUpperCase()}`} +

- {`Calories - ${prediction.calories}`} - {`Serving size - ${prediction.servingSizeG} g`} + {`Calories - ${prediction.calories}`} + {`Serving size - ${prediction.servingSizeG} g`} -

- -

+

+ +

-

Macronutrients

- {`Carbohydrates - ${prediction.macronutrients.carbohydratesTotalG} g`} - {`Protein - ${prediction.macronutrients.proteinG} g`} - {`Fat - ${prediction.macronutrients.fatTotalG} g`} - {`Saturated fat - ${prediction.macronutrients.fatSaturatedG} g`} +

Macronutrients

+ {`Carbohydrates - ${prediction.macronutrients.carbohydratesTotalG} g`} + {`Protein - ${prediction.macronutrients.proteinG} g`} + {`Fat - ${prediction.macronutrients.fatTotalG} g`} + {`Saturated fat - ${prediction.macronutrients.fatSaturatedG} g`} -

- -

+

+ +

-

Micronutrients

+

Micronutrients

{`Sodium - ${prediction.micronutrients.sodiumMG} mg`} {`Potassium - ${prediction.micronutrients.potassiumMG} mg`} @@ -49,6 +111,14 @@ const NutrientsInfo = () => { {`Fiber - ${prediction.micronutrients.fiberG} g`} {`Sugar - ${prediction.micronutrients.sugarG} g`} + +

+ +

+ + + +
) diff --git a/src/contexts/signed-in/nutrition-tracker/nutrition-tracker.context.js b/src/contexts/signed-in/nutrition-tracker/nutrition-tracker.context.js index f29ccc2..2d53f88 100644 --- a/src/contexts/signed-in/nutrition-tracker/nutrition-tracker.context.js +++ b/src/contexts/signed-in/nutrition-tracker/nutrition-tracker.context.js @@ -1,6 +1,6 @@ import { createContext, useState, useEffect, useContext } from "react"; -import { validateAddDayTracked, validateUpdateDayTracked, +import { validatePredictionInfo, validateAddDayTracked, validateUpdateDayTracked, validateFilterNutritionTrackedDays, validateRemoveNutritionTrackedDay } from "../../../utils/validations/nutrition-tracker.validations"; import { calculateSummary } from "../../../utils/calculations/nutrition-tracker.calculations"; @@ -17,6 +17,24 @@ import { getNutritionTrackedDaysData, getNutritionTrackedDaysSummaryData, // TODO: sort the records by date // helper functions +const addDayTrackedFromPredictionHelper = (nutritionTrackedDays, predictionNutritionInfo) => { + if (validatePredictionInfo(nutritionTrackedDays, predictionNutritionInfo)) return nutritionTrackedDays + + return [ + ...nutritionTrackedDays, + { + dateTracked: String(predictionNutritionInfo.dateTracked), + calories: Number(predictionNutritionInfo.calories), + macronutrients: { + carbohydrates: Number(predictionNutritionInfo.macronutrients.carbohydrates), + protein: Number(predictionNutritionInfo.macronutrients.protein), + fat: Number(predictionNutritionInfo.macronutrients.fat), + }, + micronutrients: predictionNutritionInfo.micronutrients + } + ] +} + const addMicronutrientsToTrackedDayInfoHelper = (formInputMicronutrients, trackedDayInfo) => { return { ...trackedDayInfo, @@ -215,6 +233,8 @@ export const NutritionTrackerContext = createContext({ updateFormInputMicronutrients: () => {}, deleteFormInputMicronutrients: () => {}, + addDayTrackedFromPrediction: () => {}, + nutritionTrackedDaysSummary: {}, // nutritionTrackedDaysSummary structure: // { @@ -360,13 +380,19 @@ export const NutritionTrackerProvider = ({ children }) => { putNutritionTrackedDaysSummary(currentUser.uid, currentUser.email, nutritionTrackedDaysSummary); }; + const addDayTrackedFromPrediction = (predictionNutritionInfo) => { + setNutritionTrackedDays(addDayTrackedFromPredictionHelper(nutritionTrackedDays, predictionNutritionInfo)) + setFormInputMicronutrients([]); + } + const value = { nutritionTrackedDays, formInputMicronutrients, addDayTracked, updateDayTracked, getDayTracked, addFormInputMicronutrients, updateFormInputMicronutrients, deleteFormInputMicronutrients, nutritionTrackedDaysSummary, nutritionTrackedDaysView, dayTrackedSearchResult, filterDayTracked, removeDayTracked, clearDayTrackedFilter, - setDefaultNutritionTrackedDaysValues, setDefaultNutritionTrackedDaysSummaryValues, updateNutritionTrackedDaysAndSummary } + setDefaultNutritionTrackedDaysValues, setDefaultNutritionTrackedDaysSummaryValues, updateNutritionTrackedDaysAndSummary, + addDayTrackedFromPrediction } return ( { + if (validatePredictionInfo(nutritionTrackedDays, predictionNutritionInfo)) return nutritionTrackedDays + + return [ + ...nutritionTrackedDays, + { + dateTracked: String(predictionNutritionInfo.dateTracked), + calories: Number(predictionNutritionInfo.calories), + macronutrients: { + carbohydrates: Number(predictionNutritionInfo.macronutrients.carbohydrates), + protein: Number(predictionNutritionInfo.macronutrients.protein), + fat: Number(predictionNutritionInfo.macronutrients.fat), + }, + micronutrients: predictionNutritionInfo.micronutrients + } + ] +} + const addMicronutrientsToTrackedDayInfoHelper = (formInputMicronutrients, trackedDayInfo) => { return { ...trackedDayInfo, @@ -181,6 +199,8 @@ export const NutritionTrackerContext = createContext({ updateFormInputMicronutrients: () => {}, deleteFormInputMicronutrients: () => {}, + addDayTrackedFromPrediction: () => {}, + nutritionTrackedDaysSummary: {}, // nutritionTrackedDaysSummary structure: // { @@ -272,12 +292,18 @@ export const NutritionTrackerProvider = ({ children }) => { setNutritionTrackedDaysView(nutritionTrackedDays) } + const addDayTrackedFromPrediction = (predictionNutritionInfo) => { + setNutritionTrackedDays(addDayTrackedFromPredictionHelper(nutritionTrackedDays, predictionNutritionInfo)) + setFormInputMicronutrients([]); + } + const value = { nutritionTrackedDays, formInputMicronutrients, addDayTracked, updateDayTracked, getDayTracked, addFormInputMicronutrients, updateFormInputMicronutrients, deleteFormInputMicronutrients, nutritionTrackedDaysSummary, nutritionTrackedDaysView, dayTrackedSearchResult, - filterDayTracked, removeDayTracked, clearDayTrackedFilter } + filterDayTracked, removeDayTracked, clearDayTrackedFilter, + addDayTrackedFromPrediction } return ( { + if (validatePredictionInfo(nutritionTrackedDays, predictionNutritionInfo)) return nutritionTrackedDays + + return [ + ...nutritionTrackedDays, + { + dateTracked: String(predictionNutritionInfo.dateTracked), + calories: Number(predictionNutritionInfo.calories), + macronutrients: { + carbohydrates: Number(predictionNutritionInfo.macronutrients.carbohydrates), + protein: Number(predictionNutritionInfo.macronutrients.protein), + fat: Number(predictionNutritionInfo.macronutrients.fat), + }, + micronutrients: predictionNutritionInfo.micronutrients + } + ] +} + const addMicronutrientsToTrackedDayInfoHelper = (formInputMicronutrients, trackedDayInfo) => { return { ...trackedDayInfo, @@ -191,4 +209,10 @@ export const setNutritionTrackedDaysView = (nutritionTrackedDaysView) => { export const setNutritionTrackedDaysSummary = (nutritionTrackedDaysSummary) => { return createAction(NUTRITION_TRACKER_ACTION_TYPES.SET_NUTRITION_TRACKED_DAYS_SUMMARY, nutritionTrackedDaysSummary) +} + +export const addDayTrackedFromPrediction = (nutritionTrackedDays, predictionNutritionInfo) => { + const newNutritionTrackedDays = addDayTrackedFromPredictionHelper(nutritionTrackedDays, predictionNutritionInfo) + + return createAction(NUTRITION_TRACKER_ACTION_TYPES.SET_NUTRITION_TRACKED_DAYS, newNutritionTrackedDays) } \ No newline at end of file diff --git a/src/utils/validations/nutrition-tracker.validations.js b/src/utils/validations/nutrition-tracker.validations.js index d7841b4..762aca1 100644 --- a/src/utils/validations/nutrition-tracker.validations.js +++ b/src/utils/validations/nutrition-tracker.validations.js @@ -5,6 +5,74 @@ import { REGEX_PATTERNS } from "./regex.constants"; // nutrition tracker validation functions +export const validatePredictionInfo = (nutritionTrackedDays, predictionNutritionInfo) => { + // check that trackedDayInfo's day doesn't exist in nutritionTrackedDays + const trackedDayExists = nutritionTrackedDays.find((nutritionTrackedDay) => { + return nutritionTrackedDay.dateTracked === predictionNutritionInfo.dateTracked; + }); + + if (trackedDayExists) { + errorOnTrackedDayExists(); + + return true; + } + + // check if macronutrients data types are valid + if (!(REGEX_PATTERNS.floatNumbers.test(String(predictionNutritionInfo.calories))) || + Number(predictionNutritionInfo.calories) < 0 || + !(REGEX_PATTERNS.floatNumbers.test(String(predictionNutritionInfo.macronutrients.carbohydrates))) || + Number(predictionNutritionInfo.macronutrients.carbohydrates) < 0 || + !(REGEX_PATTERNS.floatNumbers.test(String(predictionNutritionInfo.macronutrients.protein))) || + Number(predictionNutritionInfo.macronutrients.protein) < 0 || + !(REGEX_PATTERNS.floatNumbers.test(String(predictionNutritionInfo.macronutrients.fat))) || + Number(predictionNutritionInfo.macronutrients.fat) < 0) { + + errorOnInvalidMacronutrientInputs(); + + return true; + } + + // check if micronutrients are valid + // check if micronutrients data types are valid + const invalidMicronutrients = predictionNutritionInfo.micronutrients.find(micronutrient => { + if (String(micronutrient.name).length > 50 || String(micronutrient.unit).length > 5 ) { + + errorOnInvalidMicronutrientInput(); + + return true; + } + + if (!(REGEX_PATTERNS.floatNumbers.test(String(micronutrient.amount))) || + Number(micronutrient.amount) < 0) { + + errorOnInvalidMicronutrientInput(); + + return true; + } + + return false; + }); + + if (invalidMicronutrients) return true; + + // check if micronutrients are not empty + const emptyMicronutrients = predictionNutritionInfo.micronutrients.find(micronutrient => { + if (String(micronutrient.name) === "" || String(micronutrient.amount) === "" || + String(micronutrient.unit) === "") { + + errorOnEmptyMicronutrients(); + + return true; + } + + return false; + }); + + if (emptyMicronutrients) return true; + + return false; +} + export const validateAddDayTracked = (nutritionTrackedDays, trackedDayInfo) => { // check that trackedDayInfo's day doesn't exist in nutritionTrackedDays const trackedDayExists = nutritionTrackedDays.find((nutritionTrackedDay) => { @@ -71,7 +139,7 @@ export const validateAddDayTracked = (nutritionTrackedDays, trackedDayInfo) => { if (emptyMicronutrients) return true; return false; -}; +} export const validateUpdateDayTracked = (nutritionTrackedDays, updatedTrackedDayInfo) => { // check that updatedTrackedDayInfo exists in nutritionTrackedDays